I'm having a strange issue with an Android game I'm making. It's a 2d action puzzler; the sprites and background are simple opaque images, but need to move quickly and smoothly. I have been experiencing general frame rate issues however, and this particular issue is quite confusing to me though.
The problem is that I can start my game (on the hardware, a Nexus 7) and have it run at a frame rate of at least 60fps, then close it and start it back up and it will run at 30-45. Repeating this process, the game usually runs with the slower framerate, but runs perfectly smoothly on every 4th (or so) attempt.
I am new to Android and programming in general, so Im wondering if anyone has a guess as to what could be causing this performance discrepancy on repeated runs? I can edit to include some code if it's helpful, but this seems more theoretical and Im not sure what would be relevant to post.
Thanks.
EDIT:
Here's my onCreate code:
import android.app.Activity;
import android.content.Context;
import android.content.res.Configuration;
import android.graphics.Bitmap;
import android.graphics.Bitmap.Config;
import android.os.Bundle;
import android.os.PowerManager;
import android.os.PowerManager.WakeLock;
import android.view.Window;
import android.view.WindowManager;
import com.jag.framework.Audio;
import com.jag.framework.FileIO;
import com.jag.framework.Game;
import com.jag.framework.Graphics;
import com.jag.framework.Input;
import com.jag.framework.Screen;
public abstract class AndroidGame extends Activity implements Game {
AndroidFastRenderView renderView;
Graphics graphics;
Audio audio;
Input input;
FileIO fileIO;
Screen screen;
WakeLock wakeLock;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
requestWindowFeature(Window.FEATURE_NO_TITLE);
getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,
WindowManager.LayoutParams.FLAG_FULLSCREEN);
boolean isPortrait = getResources().getConfiguration().orientation == Configuration.ORIENTATION_PORTRAIT;
int frameBufferWidth = isPortrait ? 800: 1200;
int frameBufferHeight = isPortrait ? 1200: 800;
Bitmap frameBuffer = Bitmap.createBitmap(frameBufferWidth,
frameBufferHeight, Config.RGB_565);
float scaleX = (float) frameBufferWidth
/ getWindowManager().getDefaultDisplay().getWidth();
float scaleY = (float) frameBufferHeight
/ getWindowManager().getDefaultDisplay().getHeight();
renderView = new AndroidFastRenderView(this, frameBuffer);
graphics = new AndroidGraphics(getAssets(), frameBuffer);
fileIO = new AndroidFileIO(this);
audio = new AndroidAudio(this);
input = new AndroidInput(this, renderView, scaleX, scaleY);
screen = getInitScreen();
setContentView(renderView);
PowerManager powerManager = (PowerManager) getSystemService(Context.POWER_SERVICE);
wakeLock = powerManager.newWakeLock(PowerManager.FULL_WAKE_LOCK, "MyGame");
}
#Override
public void onResume() {
super.onResume();
wakeLock.acquire();
screen.resume();
renderView.resume();
}
#Override
public void onPause() {
super.onPause();
wakeLock.release();
renderView.pause();
screen.pause();
if (isFinishing())
screen.dispose();
}
#Override
public Input getInput() {
return input;
}
#Override
public FileIO getFileIO() {
return fileIO;
}
#Override
public Graphics getGraphics() {
return graphics;
}
#Override
public Audio getAudio() {
return audio;
}
#Override
public void setScreen(Screen screen) {
if (screen == null)
throw new IllegalArgumentException("Screen must not be null");
this.screen.pause();
this.screen.dispose();
screen.resume();
screen.update(0);
this.screen = screen;
}
public Screen getCurrentScreen() {
return screen;
}
}
and here's my main code:
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Random;
import android.content.res.AssetManager;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Paint.Style;
import android.graphics.Rect;
import com.jag.framework.Game;
import com.jag.framework.Graphics;
import com.jag.framework.Image;
import com.jag.framework.Input.TouchEvent;
import com.jag.framework.Screen;
public class GameScreen extends Screen {
enum GameState {
Ready, Running, Paused, GameOver
}
GameState state = GameState.Ready;
// Variable Setup
private static Scene scene;
public static int screenheight;
private ArrayList<Pieces> pieces;
//lanes:
int lane;
Paint paint, paint2;
Rect rect;
Image fore, rings1, rings2, base1, base2;
boolean recent, freeze, touch, pospressed, negpressed, wrongbutton;
int timepassed, difficulty, recentinterval, score;
Bitmap bitmap;
Canvas canvas2;
Rect foreg;
InputStream in;
AssetManager assets;
public GameScreen(Game game) {
super(game);
// Initialize game objects here
scene = new Scene(600);
screenheight = game.getGraphics().getHeight();
pieces = new ArrayList<Pieces>();
lane = 100;
recent = true;
Pieces p3 = new Pieces(lane, 940, true);
pieces.add(p3);
paint = new Paint();
paint.setTextSize(30);
paint.setTextAlign(Paint.Align.CENTER);
paint.setAntiAlias(true);
paint.setColor(Color.WHITE);
paint2 = new Paint();
paint2.setColor(Color.WHITE);
paint2.setStyle(Style.FILL);
difficulty = 50;
recentinterval = 30;
timepassed = 0;
freeze = false;
fore = Assets.block;
rings1 = Assets.ringswhite;
rings2 = Assets.ringsblack;
base1 = Assets.basewhite;
base2 = Assets.baseblack;
wrongbutton = false;
score = 0;
}
#Override
public void update(float deltaTime) {
List<TouchEvent> touchEvents = game.getInput().getTouchEvents();
// We have four separate update methods in this example.
// Depending on the state of the game, we call different update methods.
// Refer to Unit 3's code. We did a similar thing without separating the
// update methods.
if (state == GameState.Ready)
updateReady(touchEvents);
if (state == GameState.Running)
updateRunning(touchEvents, deltaTime);
if (state == GameState.Paused)
updatePaused(touchEvents);
if (state == GameState.GameOver)
updateGameOver(touchEvents);
}
private void updateReady(List<TouchEvent> touchEvents) {
// This example starts with a "Ready" screen.
// When the user touches the screen, the game begins.
// state now becomes GameState.Running.
// Now the updateRunning() method will be called!
if (touchEvents.size() > 0) {
game.getGraphics().clearScreen(Color.BLACK);
state = GameState.Running;
Assets.theme.stop();
}
}
private void updateRunning(List<TouchEvent> touchEvents, float deltaTime) {
// 1. All touch input is handled here:
int len = touchEvents.size();
for (int i = 0; i < len; i++) {
TouchEvent event = touchEvents.get(i);
if ((event.type == TouchEvent.TOUCH_DRAGGED || event.type == TouchEvent.TOUCH_DOWN) && event.y <= 1000) {
touch = true;
scene.setLine(event.x);
}
//POSITIVE BUTTON
if (event.type == TouchEvent.TOUCH_DOWN && ((0 < event.x) && (event.x < 250)) && event.y > 1000) {
pospressed = true;
}
//NEGATIVE BUTTON
if (event.type == TouchEvent.TOUCH_DOWN && ((550 < event.x) && (event.x < 800)) && event.y > 1000) {
negpressed = true;
}
//ACTIVATES ALERT (reduce score)
if (event.type == TouchEvent.TOUCH_UP) {
touch = false;
pospressed = false;
negpressed = false;
}
}
// 2. Check miscellaneous events like death:
// if (livesLeft == 0) {
// state = GameState.GameOver;
// }
// 3. Call individual update() methods here.
// This is where all the game updates happen.
// For example, robot.update();
if (!freeze){
timepassed += 1;
if ((timepassed % recentinterval) == 0){
recent = false;
}
}
Random randomGenerator = new Random();
int randomInt = randomGenerator.nextInt(difficulty);
int randomInt2 = randomGenerator.nextInt(7);
boolean randomBool = randomGenerator.nextBoolean();
int chanceOfNewPiece = 8;
if ((randomInt < chanceOfNewPiece)&&!recent) {
Pieces p = new Pieces((randomInt2+1)*lane, 940, randomBool);
pieces.add(p);
recent = true;
}
Iterator<Pieces> it = pieces.iterator();
while (it.hasNext()) {
Pieces p = it.next();
if (p.isVisible()&&!p.wayback&&!freeze)
p.update();
else if (p.isVisible()&&!p.wayback&&freeze){
p.still();
}
else if (p.wayback && pospressed){
if (score > 10){
score -= 10;
}
if (score <= 10){
score = 0;
}
if (p.type){
it.remove();
freeze = false;
pospressed = false;
Assets.click.play(100);
}
if (!p.type){
freeze = false;
pospressed = false;
wrongbutton = true;
}
}
else if (p.wayback && negpressed){
if (score > 10){
score -= 10;
}
if (score <= 10){
score = 0;
}
if (p.type){
freeze = false;
negpressed = false;
wrongbutton = true;
}
if (!p.type){
it.remove();
freeze = false;
negpressed = false;
Assets.click.play(100);
}
}
else if (p.isVisible() && p.wayback && wrongbutton){
p.updateback();
}
else if (p.isVisible()&&p.wayback){
p.updateback();
freeze = true;
}
else if (p.y < 10){
score += 1;
it.remove();
freeze = false;
wrongbutton = false;
}
else {
// if (p.y > screenheight-281){
// game.getGraphics().clearScreen(Color.BLACK);
// state = GameState.GameOver;
// }
score = 0;
it.remove();
freeze = false;
wrongbutton = false;
}
}
// if (pieces.size() == 0) {
//
// game.getGraphics().clearScreen(Color.BLACK);
// state = GameState.GameOver;
// }
}
private void updatePaused(List<TouchEvent> touchEvents) {
int len = touchEvents.size();
for (int i = 0; i < len; i++) {
TouchEvent event = touchEvents.get(i);
if (event.type == TouchEvent.TOUCH_UP) {
state = GameState.Running;
Assets.theme.stop();
}
}
}
private void updateGameOver(List<TouchEvent> touchEvents) {
int len = touchEvents.size();
for (int i = 0; i < len; i++) {
TouchEvent event = touchEvents.get(i);
if (event.type == TouchEvent.TOUCH_UP) {
nullify();
game.setScreen(new MainMenuScreen(game));
return;
}
}
}
#Override
public void paint(float deltaTime) {
//Debug.startMethodTracing();
Graphics g = game.getGraphics();
// draw the game elements
if (state == GameState.Running){
int fingerx = scene.getLine();
g.drawRect(fingerx, 0, g.getWidth(), 1200, Color.BLACK);
g.drawRect(0, 0, fingerx, 1200, Color.WHITE);
if (touch == false){
g.drawRect(250, 1000, 302, 205, Color.RED);
}
g.drawScaledImage(base1, fingerx, 955, g.getWidth()-fingerx,
base2.getHeight(), fingerx, 0, g.getWidth()-fingerx, base2.getHeight());
g.drawScaledImage(base2, 0, 955, fingerx, base2.getHeight(), 0, 0, fingerx, base2.getHeight());
g.drawString(String.valueOf(score),
350, 1075, paint);
////g.drawImage(back, 0, 0);
// g.drawScaledImage(Assets.fore, fingerx, 0, g.getWidth()-fingerx,
// Assets.fore.getHeight(), fingerx, 0, g.getWidth()-fingerx, Assets.fore.getHeight());
////g.drawImage(Assets.topwhite, 0, 0);
//g.saveCanvas();
//g.drawTransRect(0, 0, fingerx, g.getHeight());
////g.drawImage(fore, 0, 0);
//g.drawImage(base2, 0, 955);
// g.drawScaledImage(Assets.back, 0, 0, fingerx, Assets.back.getHeight(), 0, 0, fingerx, base2.getHeight());
// g.drawScaledImage(base2, fingerx, 955, g.getWidth()-fingerx, base2.getHeight(),
// fingerx, 0, g.getWidth()-fingerx, base2.getHeight());
//g.drawCropped(base2);
////g.drawImage(rings2, 0, 950);
//g.restoreCanvas();
for (Pieces p : pieces){
if (p.type == true)
g.drawImage(Assets.pos, (p.x - 40), p.y);
if (p.type == false){
g.drawImage(Assets.neg, (p.x - 40), p.y);
}
}
}
// draw the UI
if (state == GameState.Ready)
drawReadyUI();
if (state == GameState.Running)
drawRunningUI();
if (state == GameState.Paused)
drawPausedUI();
if (state == GameState.GameOver)
drawGameOverUI();
//Debug.stopMethodTracing();
}
private void nullify() {
// Set all variables to null. You will be recreating them in the
// constructor.
paint = null;
scene = null;
pieces = null;
scene = null;
pieces = null;
paint2 = null;
Assets.theme = null;
Assets.click = null;
// Call garbage collector to clean up memory.
System.gc();
}
private void drawReadyUI() {
Graphics g = game.getGraphics();
g.drawARGB(155, 0, 0, 0);
g.drawString("TOUCH THE SCREEN YA DUMMY",
400, 300, paint);
}
private void drawRunningUI() {
//Graphics g = game.getGraphics();
}
private void drawPausedUI() {
Graphics g = game.getGraphics();
// Darken the entire screen so you can display the Paused screen.
g.drawRect(0, 0, 801, 1281, Color.BLACK);
g.drawString("HEY GUY IT'S PAUSED", 640, 300, paint);
}
private void drawGameOverUI() {
Graphics g = game.getGraphics();
g.drawRect(0, 0, 1281, 801, Color.BLACK);
g.drawString("GAME OVER BRO", 640, 300, paint);
}
#Override
public void pause() {
if (state == GameState.Running)
System.gc();
state = GameState.Paused;
}
#Override
public void resume() {
}
#Override
public void dispose() {
}
#Override
public void backButton() {
pause();
}
public static Scene getScene(){
return scene;
}
public void setScore(int i){
score += i;
}
}
I realize this is a bit of a mess; it's the first thing I've coded. The framework is from kilobolt.com. Let me know if there are other classes/ methods I should include.
Related
I want my game to exit and go to a separate Activity when the user collides. For example, I want it to go back to my main menu. I've tried adding a Button, new intent method etc and these don't work. I can't find the answer anywhere and i'm not knowledgeable enough to do this as I'm learning. I've trawled the internet but nothing has really helped. Any advice would be awesome. My idea was to change the newgame() method to exit the game?
import android.content.Context;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Rect;
import android.view.MotionEvent;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
import java.util.ArrayList;
import java.util.Random;
public class GamePanel extends SurfaceView implements SurfaceHolder.Callback {
private Threading threading;
private BackgroundImage bg;
public static final int WIDTH = 856;
public static final int HEIGHT = 480;
public static final int MovementSpeed = -8;
private Player player;
private ArrayList<BorderTop> borderTop;
private ArrayList<BorderBottom> borderBottom;
private ArrayList<Smokepuff> smoke;
private ArrayList<Missile> missiles;
private int MaximumBorderHeight;
private int MinimumBorderHeight;
private boolean topDown = true;
private boolean botDown = true;
private int progressDifficulty = 20;
private Random rand = new Random();
private boolean newGameCreated;
private long smokeStartTime;
private long missileStartTime;
public GamePanel(Context context) {
super(context);
getHolder().addCallback(this);
setFocusable(true);
}
#Override
public void surfaceCreated(SurfaceHolder holder) {
bg = new BackgroundImage(BitmapFactory.decodeResource(getResources(), R.drawable.grassbg1));
player = new Player(BitmapFactory.decodeResource(getResources(), R.drawable.smallcatfinal), 40, 44, 1);
borderTop = new ArrayList<BorderTop>();
borderBottom = new ArrayList<BorderBottom>();
smoke = new ArrayList<Smokepuff>();
missiles = new ArrayList<Missile>();
smokeStartTime= System.nanoTime();
missileStartTime = System.nanoTime();
threading = new Threading(getHolder(), this);
threading.setRunning(true);
threading.start();
}
#Override
public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
}
#Override
public void surfaceDestroyed(SurfaceHolder holder) {
boolean retry = true;
int counter = 0;
while (retry && counter < 1000) {
counter++;
try {
threading.setRunning(false);
threading.join();
retry = false;
threading = null;
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
#Override
public boolean onTouchEvent(MotionEvent event) {
if (event.getAction() == MotionEvent.ACTION_DOWN) {
if (!player.getPlaying()) {
player.setPlaying(true);
player.setUp(true);
}else{
player.setUp(true);
}
return true;
}
if (event.getAction() == MotionEvent.ACTION_UP) {
player.setUp(false);
return true;
}
return super.onTouchEvent(event);
}
public void update() {
if (player.getPlaying()) {
bg.update();
player.update();
MaximumBorderHeight = 30 + player.getScore() / progressDifficulty;
if (MaximumBorderHeight > HEIGHT / 4) MaximumBorderHeight = HEIGHT / 4;
MinimumBorderHeight = 5 + player.getScore() / progressDifficulty;
//check bottom border collision
for (int i = 0; i < borderBottom.size(); i++) {
if (collision(borderBottom.get(i), player))
player.setPlaying(false);
}
//check top border collision
for (int i = 0; i < borderTop.size(); i++) {
if (collision(borderTop.get(i), player))
player.setPlaying(false);
}
this.updateTop();
this.updateBottom();
long missileElapsed = (System.nanoTime() - missileStartTime) / 1000000;
if (missileElapsed > (2000 - player.getScore() / 4)) {
//first missile always goes down the middle
if (missiles.size() == 0) {
missiles.add(new Missile(BitmapFactory.decodeResource(getResources(), R.drawable.
missile), WIDTH + 10, HEIGHT / 2, 45, 15, player.getScore(), 13));
} else {
missiles.add(new Missile(BitmapFactory.decodeResource(getResources(), R.drawable.missile),
WIDTH + 10, (int) (rand.nextDouble() * (HEIGHT - (MaximumBorderHeight * 2)) + MaximumBorderHeight), 45, 15, player.getScore(), 13));
}
//reset timer
missileStartTime = System.nanoTime();
}
//loop through every missile and check collision and remove
for (int i = 0; i < missiles.size(); i++) {
//update missile
missiles.get(i).update();
if (collision(missiles.get(i), player)) {
missiles.remove(i);
player.setPlaying(false);
break;
}
//remove missile if it is way off the screen
if (missiles.get(i).getX() < -100) {
missiles.remove(i);
break;
}
}
long elapsed = (System.nanoTime() - smokeStartTime) / 1000000;
if (elapsed > 120) {
smoke.add(new Smokepuff(player.getX(), player.getY() + 10));
smokeStartTime = System.nanoTime();
}
for (int i = 0; i < smoke.size(); i++) {
smoke.get(i).update();
if (smoke.get(i).getX() < -10) {
smoke.remove(i);
}
}
} else {
newGameCreated = false;
if (!newGameCreated) {
newGame();
}
}
public boolean collision (GameObject a, GameObject b)
{
if (Rect.intersects(a.getRectangle(), b.getRectangle())) {
return true;
}
return false;
}
#Override
public void draw(Canvas canvas) {
super.draw(canvas);
final float ScaleX = (float) getWidth() / WIDTH*1.f;
final float ScaleY = (float) getHeight() / HEIGHT*1.f;
if (canvas != null) {
final int savedState = canvas.save();
canvas.scale(ScaleX, ScaleY);
bg.draw(canvas);
player.draw(canvas);
for(Smokepuff sp: smoke)
{
sp.draw(canvas);
}
//draw missiles
for(Missile m: missiles)
{
m.draw(canvas);
}
for(BorderTop bt: borderTop)
{
bt.draw(canvas);
}
for(BorderBottom bb: borderBottom)
{
bb.draw(canvas);
}
canvas.restoreToCount(savedState);
}
}
public void updateBottom() {
if (player.getScore() % 40 == 0) {
borderBottom.add(new BorderBottom(BitmapFactory.decodeResource(getResources(), R.drawable.brick),
borderBottom.get(borderBottom.size() - 1).getX() + 20, (int) ((rand.nextDouble()
* MaximumBorderHeight) + (HEIGHT - MaximumBorderHeight))));
}
//update bottom border
for (int i = 0; i < borderBottom.size(); i++) {
borderBottom.get(i).update();
//if border is moving off screen, remove it and add a corresponding new one
if (borderBottom.get(i).getX() < -20) {
borderBottom.remove(i);
//determine if border will be moving up or down
if (borderBottom.get(borderBottom.size() - 1).getY() <= HEIGHT - MaximumBorderHeight) {
botDown = true;
}
if (borderBottom.get(borderBottom.size() - 1).getY() >= HEIGHT - MinimumBorderHeight) {
botDown = false;
}
if (botDown) {
borderBottom.add(new BorderBottom(BitmapFactory.decodeResource(getResources(),R.drawable.brick
), borderBottom.get(borderBottom.size() - 1).getX() + 20, borderBottom.get(borderBottom.size() - 1
).getY() + 1));
} else {
borderBottom.add(new BorderBottom(BitmapFactory.decodeResource(getResources(), R.drawable.brick
), borderBottom.get(borderBottom.size() - 1).getX() + 20, borderBottom.get(borderBottom.size() - 1
).getY() - 1));
}
}
}
}
public void updateTop()
{
if (player.getScore() % 50 == 0) {
borderTop.add(new BorderTop(BitmapFactory.decodeResource(getResources(), R.drawable.brick), borderTop.get(borderTop.size() - 1).getX() + 20, 0, (int) ((rand.nextDouble() * (MaximumBorderHeight)) + 1)));
}
for (int i = 0; i < borderTop.size(); i++) {
borderTop.get(i).update();
if (borderTop.get(i).getX() < -20) {
borderTop.remove(i);
if (borderTop.get(borderTop.size() - 1).getHeight() >= MaximumBorderHeight) {
topDown = false;
}
if (borderTop.get(borderTop.size() - 1).getHeight() <= MinimumBorderHeight) {
topDown = true;
}
if (topDown) {
borderTop.add(new BorderTop(BitmapFactory.decodeResource(getResources(), R.drawable.brick), borderTop.get(borderTop.size() - 1).getX() + 20, 0, borderTop.get(borderTop.size() - 1).getHeight() + 1));
} else {
borderTop.add(new BorderTop(BitmapFactory.decodeResource(getResources(), R.drawable.brick), borderTop.get(borderTop.size() - 1).getX() + 20, 0, borderTop.get(borderTop.size() - 1).getHeight() - 1));
}
}
}
}
public void newGame() {
borderBottom.clear();
borderTop.clear();
missiles.clear();
smoke.clear();
MinimumBorderHeight = 5;
MaximumBorderHeight = 30;
player.resetDY();
player.resetScore();
player.setY(HEIGHT/2);
//create initial borders
//initial top border
for(int i = 0; i*20<WIDTH+40;i++)
{
// first top border create
if(i==0)
{borderTop.add(new BorderTop(BitmapFactory.decodeResource(getResources(),R.drawable.brick
),i*20,0, 10));
}
else
{
borderTop.add(new BorderTop(BitmapFactory.decodeResource(getResources(),R.drawable.brick
),i*20,0, borderTop.get(i-1).getHeight()+1));
}
}
//initial bottom border
for(int i = 0; i*20<WIDTH+40; i++)
{
//first border ever created
if(i==0)
{
borderBottom.add(new BorderBottom(BitmapFactory.decodeResource(getResources(),R.drawable.brick)
,i*20,HEIGHT - MinimumBorderHeight));
}
//adding borders until the initial screen is filed
else
{
borderBottom.add(new BorderBottom(BitmapFactory.decodeResource(getResources(), R.drawable.brick),
i * 20, borderBottom.get(i - 1).getY() - 1));
}
}
newGameCreated = true;
}
}
About my problem: What is the way to get textures with alpha channel to work?
I use a png texture with alpha channel, but the result is black color on transparent area.
Android program result:
enter image description here
3ds model in cheetah3d:
enter image description here
This is my code:
package testoc.biz.testproject;
import android.app.Activity;
import android.opengl.GLSurfaceView;
import android.os.Bundle;
import android.view.MotionEvent;
import com.threed.jpct.Camera;
import com.threed.jpct.FrameBuffer;
import com.threed.jpct.Light;
import com.threed.jpct.Loader;
import com.threed.jpct.Logger;
import com.threed.jpct.Matrix;
import com.threed.jpct.Object3D;
import com.threed.jpct.Primitives;
import com.threed.jpct.RGBColor;
import com.threed.jpct.SimpleVector;
import com.threed.jpct.Texture;
import com.threed.jpct.TextureManager;
import com.threed.jpct.World;
import com.threed.jpct.util.BitmapHelper;
import com.threed.jpct.util.MemoryHelper;
import java.io.InputStream;
import java.lang.reflect.Field;
import javax.microedition.khronos.egl.EGL10;
import javax.microedition.khronos.egl.EGLConfig;
import javax.microedition.khronos.egl.EGLDisplay;
import javax.microedition.khronos.opengles.GL10;
public class FullscreenActivity extends Activity {
// Used to handle pause and resume...
private static FullscreenActivity master = null;
private GLSurfaceView mGLView;
private MyRenderer renderer = null;
private FrameBuffer fb = null;
private World world = null;
private RGBColor back = new RGBColor(50, 50, 100);
private float touchTurn = 0;
private float touchTurnUp = 0;
private float xpos = -1;
private float ypos = -1;
private Object3D cube = null;
private int fps = 0;
private boolean gl2 = true;
private Light sun = null;
protected void onCreate(Bundle savedInstanceState) {
if (master != null) {
copy(master);
}
super.onCreate(savedInstanceState);
mGLView = new GLSurfaceView(getApplication());
if (gl2) {
mGLView.setEGLContextClientVersion(2);
} else {
mGLView.setEGLConfigChooser(new GLSurfaceView.EGLConfigChooser() {
public EGLConfig chooseConfig(EGL10 egl, EGLDisplay display) {
int[] attributes = new int[] { EGL10.EGL_DEPTH_SIZE, 16, EGL10.EGL_NONE };
EGLConfig[] configs = new EGLConfig[1];
int[] result = new int[1];
egl.eglChooseConfig(display, attributes, configs, 1, result);
return configs[0];
}
});
}
renderer = new MyRenderer();
mGLView.setRenderer(renderer);
setContentView(mGLView);
}
#Override
protected void onPause() {
super.onPause();
mGLView.onPause();
}
#Override
protected void onResume() {
super.onResume();
mGLView.onResume();
}
#Override
protected void onStop() {
super.onStop();
}
private void copy(Object src) {
try {
Logger.log("Copying data from master Activity!");
Field[] fs = src.getClass().getDeclaredFields();
for (Field f : fs) {
f.setAccessible(true);
f.set(this, f.get(src));
}
} catch (Exception e) {
throw new RuntimeException(e);
}
}
public boolean onTouchEvent(MotionEvent me) {
if (me.getAction() == MotionEvent.ACTION_DOWN) {
xpos = me.getX();
ypos = me.getY();
return true;
}
if (me.getAction() == MotionEvent.ACTION_UP) {
xpos = -1;
ypos = -1;
touchTurn = 0;
touchTurnUp = 0;
return true;
}
if (me.getAction() == MotionEvent.ACTION_MOVE) {
float xd = me.getX() - xpos;
float yd = me.getY() - ypos;
xpos = me.getX();
ypos = me.getY();
touchTurn = xd / -100f;
touchTurnUp = yd / -100f;
return true;
}
try {
Thread.sleep(15);
} catch (Exception e) {
// No need for this...
}
return super.onTouchEvent(me);
}
protected boolean isFullscreenOpaque() {
return true;
}
class MyRenderer implements GLSurfaceView.Renderer {
private long time = System.currentTimeMillis();
public MyRenderer() {
}
private Object3D loadModel( float scale){
InputStream stream = getResources().openRawResource(R.raw.cube);
Object3D[] model = Loader.load3DS(stream, scale);
Object3D o3d = new Object3D(0);
Object3D temp = null;
for (int i = 0; i < model.length; i++) {
temp = model[i];
temp.setCenter(SimpleVector.ORIGIN);
temp.rotateX((float)( -0.5*Math.PI));
temp.rotateMesh();
temp.setRotationMatrix(new Matrix());
o3d = Object3D.mergeObjects(o3d, temp);
o3d.build();
}
return o3d;
}
public void onSurfaceChanged(GL10 gl, int w, int h) {
if (fb != null) {
fb.dispose();
}
if (gl2) {
fb = new FrameBuffer(w, h); // OpenGL ES 2.0 constructor
} else {
fb = new FrameBuffer(gl, w, h); // OpenGL ES 1.x constructor
}
if (master == null) {
world = new World();
world.setAmbientLight(20, 20, 20);
sun = new Light(world);
sun.setIntensity(250, 250, 250);
Texture texture = new Texture(BitmapHelper.rescale(BitmapHelper.convert(getResources().getDrawable(R.drawable.gradient)), 256, 256),true);
TextureManager.getInstance().addTexture("gradient.png", texture);
cube = loadModel(20);
cube.strip();
cube.build();
world.addObject(cube);
Camera cam = world.getCamera();
cam.moveCamera(Camera.CAMERA_MOVEOUT, 50);
cam.lookAt(cube.getTransformedCenter());
SimpleVector sv = new SimpleVector();
sv.set(cube.getTransformedCenter());
sv.y -= 100;
sv.z -= 100;
sun.setPosition(sv);
MemoryHelper.compact();
if (master == null) {
Logger.log("Saving master Activity!");
master = FullscreenActivity.this;
}
}
}
public void onSurfaceCreated(GL10 gl, EGLConfig config) {
}
public void onDrawFrame(GL10 gl) {
if (touchTurn != 0) {
cube.rotateY(touchTurn);
touchTurn = 0;
}
if (touchTurnUp != 0) {
cube.rotateX(touchTurnUp);
touchTurnUp = 0;
}
fb.clear(back);
world.renderScene(fb);
world.draw(fb);
fb.display();
if (System.currentTimeMillis() - time >= 1000) {
Logger.log(fps + "fps");
fps = 0;
time = System.currentTimeMillis();
}
fps++;
}
}
}
I am used the jpct library for Android.
I've nailed it with adding glEnable as wes suggested by #Reigertje:
public void onSurfaceCreated(GL10 gl, EGLConfig config) {
gl.glEnable(GL10.GL_BLEND);
gl.glBlendFunc(GL10.GL_ONE, GL10.GL_ONE_MINUS_SRC_ALPHA);
}
I am unable to create more circles which follows its own path with drawCircle .
I have used the code below which creates another circle but follows the path along the lines of 1st circle but not independent .How do I move both circles independent of each other?
I have added
c.drawCircle(ballX-100, ballY-100, 50, ballPaintyellow);
How do I make the above circle independent from the 1st circle?. I really appreciate any help.Thanks in Advance.
BouncingBallActivity.java
package com.stuffthathappens.games;
import static android.hardware.SensorManager.DATA_X;
import static android.hardware.SensorManager.DATA_Y;
import static android.hardware.SensorManager.SENSOR_ACCELEROMETER;
import static android.hardware.SensorManager.SENSOR_DELAY_GAME;
import java.util.concurrent.TimeUnit;
import android.app.Activity;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.hardware.SensorListener;
import android.hardware.SensorManager;
import android.os.Bundle;
import android.os.Vibrator;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
import android.view.SurfaceHolder.Callback;
/**
* This activity shows a ball that bounces around. The phone's
* accelerometer acts as gravity on the ball. When the ball hits
* the edge, it bounces back and triggers the phone vibrator.
*/
#SuppressWarnings("deprecation")
public class BouncingBallActivity extends Activity implements Callback, SensorListener {
private static final int BALL_RADIUS =20;
private SurfaceView surface;
private SurfaceHolder holder;
private final BouncingBallModel model = new BouncingBallModel(BALL_RADIUS);
private GameLoop gameLoop;
private Paint backgroundPaint;
private Paint ballPaint;
private SensorManager sensorMgr;
private long lastSensorUpdate = -1;
private Paint ballPaintyellow;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.bouncing_ball);
surface = (SurfaceView) findViewById(R.id.bouncing_ball_surface);
holder = surface.getHolder();
surface.getHolder().addCallback(this);
backgroundPaint = new Paint();
backgroundPaint.setColor(Color.WHITE);
ballPaint = new Paint();
ballPaint.setColor(Color.BLUE);
ballPaint.setAntiAlias(true);
ballPaintyellow = new Paint();
ballPaintyellow.setColor(Color.YELLOW);
ballPaintyellow.setAntiAlias(true);
}
#Override
protected void onPause() {
super.onPause();
model.setVibrator(null);
sensorMgr.unregisterListener(this, SENSOR_ACCELEROMETER);
sensorMgr = null;
model.setAccel(0, 0);
}
#Override
protected void onResume() {
super.onResume();
sensorMgr = (SensorManager) getSystemService(SENSOR_SERVICE);
boolean accelSupported = sensorMgr.registerListener(this,
SENSOR_ACCELEROMETER,
SENSOR_DELAY_GAME);
if (!accelSupported) {
// on accelerometer on this device
sensorMgr.unregisterListener(this, SENSOR_ACCELEROMETER);
// TODO show an error
}
// NOTE 1: you cannot get system services before onCreate()
// NOTE 2: AndroidManifest.xml must contain this line:
// <uses-permission android:name="android.permission.VIBRATE"/>
Vibrator vibrator = (Vibrator) getSystemService(Activity.VIBRATOR_SERVICE);
model.setVibrator(vibrator);
}
public void surfaceChanged(SurfaceHolder holder, int format, int width,
int height) {
model.setSize(width, height);
}
public void surfaceCreated(SurfaceHolder holder) {
gameLoop = new GameLoop();
gameLoop.start();
}
private void draw() {
// thread safety - the SurfaceView could go away while we are drawing
Canvas c = null;
try {
// NOTE: in the LunarLander they don't have any synchronization here,
// so I guess this is OK. It will return null if the holder is not ready
c = holder.lockCanvas();
// this needs to synchronize on something
if (c != null) {
doDraw(c);
}
} finally {
if (c != null) {
holder.unlockCanvasAndPost(c);
}
}
}
private void doDraw(Canvas c) {
int width = c.getWidth();
int height = c.getHeight();
c.drawRect(0, 0, width, height, backgroundPaint);
float ballX, ballY;
synchronized (model.LOCK) {
ballX = model.ballPixelX;
ballY = model.ballPixelY;
}
c.drawCircle(ballX, ballY, BALL_RADIUS, ballPaint);
c.drawCircle(ballX-100, ballY-100, 50, ballPaintyellow);
}
public void surfaceDestroyed(SurfaceHolder holder) {
try {
model.setSize(0,0);
gameLoop.safeStop();
} finally {
gameLoop = null;
}
}
private class GameLoop extends Thread {
private volatile boolean running = true;
public void run() {
while (running) {
try {
// don't like this hardcoding
TimeUnit.MILLISECONDS.sleep(5);
draw();
model.updatePhysics();
} catch (InterruptedException ie) {
running = false;
}
}
}
public void safeStop() {
running = false;
interrupt();
}
}
public void onAccuracyChanged(int sensor, int accuracy) {
}
public void onSensorChanged(int sensor, float[] values) {
if (sensor == SENSOR_ACCELEROMETER) {
long curTime = System.currentTimeMillis();
// only allow one update every 50ms, otherwise updates
// come way too fast
if (lastSensorUpdate == -1 || (curTime - lastSensorUpdate) > 50) {
lastSensorUpdate = curTime;
model.setAccel(values[DATA_X], values[DATA_Y]);
}
}
}
}
Bouncingballmodel.java
package com.stuffthathappens.games;
import java.util.concurrent.atomic.AtomicReference;
import android.os.Vibrator;
/**
* This data model tracks the width and height of the playing field along
* with the current position of a ball.
*/
public class BouncingBallModel {
// the ball speed is meters / second. When we draw to the screen,
// 1 pixel represents 1 meter. That ends up too slow, so multiply
// by this number. Bigger numbers speeds things up.
private final float pixelsPerMeter = 10;
private final int ballRadius;
// these are public, so make sure you synchronize on LOCK
// when reading these. I made them public since you need to
// get both X and Y in pairs, and this is more efficient than
// getter methods. With two getters, you'd still need to
// synchronize.
public float ballPixelX, ballPixelY;
private int pixelWidth, pixelHeight;
// values are in meters/second
private float velocityX, velocityY;
// typical values range from -10...10, but could be higher or lower if
// the user moves the phone rapidly
private float accelX, accelY;
/**
* When the ball hits an edge, multiply the velocity by the rebound.
* A value of 1.0 means the ball bounces with 100% efficiency. Lower
* numbers simulate balls that don't bounce very much.
*/
private static final float rebound = 0.8f;
// if the ball bounces and the velocity is less than this constant,
// stop bouncing.
private static final float STOP_BOUNCING_VELOCITY = 2f;
private volatile long lastTimeMs = -1;
public final Object LOCK = new Object();
private AtomicReference<Vibrator> vibratorRef =
new AtomicReference<Vibrator>();
public BouncingBallModel(int ballRadius) {
this.ballRadius = ballRadius;
}
public void setAccel(float ax, float ay) {
synchronized (LOCK) {
this.accelX = ax;
this.accelY = ay;
}
}
public void setSize(int width, int height) {
synchronized (LOCK) {
this.pixelWidth = width;
this.pixelHeight = height;
}
}
public int getBallRadius() {
return ballRadius;
}
/**
* Call this to move the ball to a particular location on the screen. This
* resets the velocity to zero, but the acceleration doesn't change so
* the ball should start falling shortly.
*/
public void moveBall(int ballX, int ballY) {
synchronized (LOCK) {
this.ballPixelX = ballX;
this.ballPixelY = ballY;
velocityX = 0;
velocityY = 0;
}
}
public void updatePhysics() {
// copy everything to local vars (hence the 'l' prefix)
float lWidth, lHeight, lBallX, lBallY, lAx, lAy, lVx, lVy;
synchronized (LOCK) {
lWidth = pixelWidth;
lHeight = pixelHeight;
lBallX = ballPixelX;
lBallY = ballPixelY;
lVx = velocityX;
lVy = velocityY;
lAx = accelX;
lAy = -accelY;
}
if (lWidth <= 0 || lHeight <= 0) {
// invalid width and height, nothing to do until the GUI comes up
return;
}
long curTime = System.currentTimeMillis();
if (lastTimeMs < 0) {
lastTimeMs = curTime;
return;
}
long elapsedMs = curTime - lastTimeMs;
lastTimeMs = curTime;
// update the velocity
// (divide by 1000 to convert ms to seconds)
// end result is meters / second
lVx += ((elapsedMs * lAx) / 1000) * pixelsPerMeter;
lVy += ((elapsedMs * lAy) / 1000) * pixelsPerMeter;
// update the position
// (velocity is meters/sec, so divide by 1000 again)
lBallX += ((lVx * elapsedMs) / 1000) * pixelsPerMeter;
lBallY += ((lVy * elapsedMs) / 1000) * pixelsPerMeter;
boolean bouncedX = false;
boolean bouncedY = false;
if (lBallY - ballRadius < 0) {
lBallY = ballRadius;
lVy = -lVy * rebound;
bouncedY = true;
} else if (lBallY + ballRadius > lHeight) {
lBallY = lHeight - ballRadius;
lVy = -lVy * rebound;
bouncedY = true;
}
if (bouncedY && Math.abs(lVy) < STOP_BOUNCING_VELOCITY) {
lVy = 0;
bouncedY = false;
}
if (lBallX - ballRadius < 0) {
lBallX = ballRadius;
lVx = -lVx * rebound;
bouncedX = true;
} else if (lBallX + ballRadius > lWidth) {
lBallX = lWidth - ballRadius;
lVx = -lVx * rebound;
bouncedX = true;
}
if (bouncedX && Math.abs(lVx) < STOP_BOUNCING_VELOCITY) {
lVx = 0;
bouncedX = false;
}
// safely copy local vars back to object fields
synchronized (LOCK) {
ballPixelX = lBallX;
ballPixelY = lBallY;
velocityX = lVx;
velocityY = lVy;
}
if (bouncedX || bouncedY) {
Vibrator v = vibratorRef.get();
if (v != null) {
v.vibrate(20L);
}
}
}
public void setVibrator(Vibrator v) {
vibratorRef.set(v);
}
}
Which view you are using has nothing to do with it ....
At the moment you have only one BouncingBallModel
private final BouncingBallModel model = new BouncingBallModel(BALL_RADIUS);
This is the one you see when you draw something. Now if you want to draw multiple balls, you will need many BouncingBallModel. So either create a BouncingBallModel model2 or make it dynamic using an array.
Then iterate over the array and draw each ball.
I tried to make a single ball bouncing to dynamic ball bouncing . Eg: here the number of circles is 50.
But I am getting error while trying to make the circles dynamic (Model) .How do I make it work and make the model/circle dynamic.In this case 50 circles ? I really appreciate any help. Thanks in Advance.
package com.stuffthathappens.games;
import static android.hardware.SensorManager.DATA_X;
import static android.hardware.SensorManager.DATA_Y;
import static android.hardware.SensorManager.SENSOR_ACCELEROMETER;
import static android.hardware.SensorManager.SENSOR_DELAY_GAME;
import java.util.concurrent.TimeUnit;
import android.app.Activity;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.hardware.SensorListener;
import android.hardware.SensorManager;
import android.os.Bundle;
import android.os.Vibrator;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
import android.view.SurfaceHolder.Callback;
/**
* This activity shows a ball that bounces around. The phone's
* accelerometer acts as gravity on the ball. When the ball hits
* the edge, it bounces back and triggers the phone vibrator.
*/
#SuppressWarnings("deprecation")
public class BouncingBallActivity extends Activity implements Callback, SensorListener {
private static final int BALL_RADIUS =20;
private SurfaceView surface;
private SurfaceHolder holder;
private GameLoop gameLoop;
private Paint backgroundPaint;
private Paint ballPaint;
private SensorManager sensorMgr;
private long lastSensorUpdate = -1;
private Paint ballPaintyellow;
private BouncingBallModel[] model;
int Totalcircles=50;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.bouncing_ball);
for (int i = 0; i < Totalcircles; i++) {
model[i] = new BouncingBallModel(BALL_RADIUS);
}
surface = (SurfaceView) findViewById(R.id.bouncing_ball_surface);
holder = surface.getHolder();
surface.getHolder().addCallback(this);
backgroundPaint = new Paint();
backgroundPaint.setColor(Color.WHITE);
ballPaint = new Paint();
ballPaint.setColor(Color.BLUE);
ballPaint.setAntiAlias(true);
ballPaintyellow = new Paint();
ballPaintyellow.setColor(Color.YELLOW);
ballPaintyellow.setAntiAlias(true);
}
#Override
protected void onPause() {
super.onPause();
for (int i = 0; i < Totalcircles; i++) {
model[i].setVibrator(null);
}
sensorMgr.unregisterListener(this, SENSOR_ACCELEROMETER);
sensorMgr = null;
for (int i = 0; i < Totalcircles; i++) {
model[i].setAccel(0, 0);
}
}
#Override
protected void onResume() {
super.onResume();
sensorMgr = (SensorManager) getSystemService(SENSOR_SERVICE);
boolean accelSupported = sensorMgr.registerListener(this,
SENSOR_ACCELEROMETER,
SENSOR_DELAY_GAME);
if (!accelSupported) {
// on accelerometer on this device
sensorMgr.unregisterListener(this, SENSOR_ACCELEROMETER);
// TODO show an error
}
// NOTE 1: you cannot get system services before onCreate()
// NOTE 2: AndroidManifest.xml must contain this line:
// <uses-permission android:name="android.permission.VIBRATE"/>
Vibrator vibrator = (Vibrator) getSystemService(Activity.VIBRATOR_SERVICE);
for (int i = 0; i < Totalcircles; i++) {
model[i].setVibrator(vibrator);
}
}
public void surfaceChanged(SurfaceHolder holder, int format, int width,
int height) {
for (int i = 0; i < Totalcircles; i++) {
model[i].setSize(width, height);
}
}
public void surfaceCreated(SurfaceHolder holder) {
gameLoop = new GameLoop();
gameLoop.start();
}
private void draw() {
// thread safety - the SurfaceView could go away while we are drawing
Canvas c = null;
try {
// NOTE: in the LunarLander they don't have any synchronization here,
// so I guess this is OK. It will return null if the holder is not ready
c = holder.lockCanvas();
// this needs to synchronize on something
if (c != null) {
doDraw(c);
}
} finally {
if (c != null) {
holder.unlockCanvasAndPost(c);
}
}
}
private void doDraw(Canvas c) {
int width = c.getWidth();
int height = c.getHeight();
c.drawRect(0, 0, width, height, backgroundPaint);
///
float ballX[]=new float[50], ballY[]=new float[50];
for (int i = 0; i < Totalcircles; i++) {
synchronized (model[i].LOCK) {
ballX[i] = model[i].ballPixelX;
ballY[i] = model[i].ballPixelY;
}
}
//
for (int i = 0; i < Totalcircles; i++) {
c.drawCircle(ballX[i], ballY[i], BALL_RADIUS, ballPaint);
}
}
public void surfaceDestroyed(SurfaceHolder holder) {
try {
for (int i = 0; i < Totalcircles; i++) {
model[i].setSize(0,0);
}
gameLoop.safeStop();
} finally {
gameLoop = null;
}
}
private class GameLoop extends Thread {
private volatile boolean running = true;
public void run() {
while (running) {
try {
// don't like this hardcoding
TimeUnit.MILLISECONDS.sleep(5);
draw();
for (int i = 0; i < Totalcircles; i++) {
model[i].updatePhysics();
}
} catch (InterruptedException ie) {
running = false;
}
}
}
public void safeStop() {
running = false;
interrupt();
}
}
public void onAccuracyChanged(int sensor, int accuracy) {
}
public void onSensorChanged(int sensor, float[] values) {
if (sensor == SENSOR_ACCELEROMETER) {
long curTime = System.currentTimeMillis();
// only allow one update every 50ms, otherwise updates
// come way too fast
if (lastSensorUpdate == -1 || (curTime - lastSensorUpdate) > 50) {
lastSensorUpdate = curTime;
for (int i = 0; i < Totalcircles; i++) {
model[i].setAccel(values[DATA_X], values[DATA_Y]);
}
}
}
}
}
model.java
package com.stuffthathappens.games;
import java.util.concurrent.atomic.AtomicReference;
import android.os.Vibrator;
/**
* This data model tracks the width and height of the playing field along
* with the current position of a ball.
*/
public class BouncingBallModel {
// the ball speed is meters / second. When we draw to the screen,
// 1 pixel represents 1 meter. That ends up too slow, so multiply
// by this number. Bigger numbers speeds things up.
private final float pixelsPerMeter = 10;
private final int ballRadius;
// these are public, so make sure you synchronize on LOCK
// when reading these. I made them public since you need to
// get both X and Y in pairs, and this is more efficient than
// getter methods. With two getters, you'd still need to
// synchronize.
public float ballPixelX, ballPixelY;
private int pixelWidth, pixelHeight;
// values are in meters/second
private float velocityX, velocityY;
// typical values range from -10...10, but could be higher or lower if
// the user moves the phone rapidly
private float accelX, accelY;
/**
* When the ball hits an edge, multiply the velocity by the rebound.
* A value of 1.0 means the ball bounces with 100% efficiency. Lower
* numbers simulate balls that don't bounce very much.
*/
private static final float rebound = 0.8f;
// if the ball bounces and the velocity is less than this constant,
// stop bouncing.
private static final float STOP_BOUNCING_VELOCITY = 2f;
private volatile long lastTimeMs = -1;
public final Object LOCK = new Object();
private AtomicReference<Vibrator> vibratorRef =
new AtomicReference<Vibrator>();
public BouncingBallModel(int ballRadius) {
this.ballRadius = ballRadius;
}
public void setAccel(float ax, float ay) {
synchronized (LOCK) {
this.accelX = ax;
this.accelY = ay;
}
}
public void setSize(int width, int height) {
synchronized (LOCK) {
this.pixelWidth = width;
this.pixelHeight = height;
}
}
public int getBallRadius() {
return ballRadius;
}
/**
* Call this to move the ball to a particular location on the screen. This
* resets the velocity to zero, but the acceleration doesn't change so
* the ball should start falling shortly.
*/
public void moveBall(int ballX, int ballY) {
synchronized (LOCK) {
this.ballPixelX = ballX;
this.ballPixelY = ballY;
velocityX = 0;
velocityY = 0;
}
}
public void updatePhysics() {
// copy everything to local vars (hence the 'l' prefix)
float lWidth, lHeight, lBallX, lBallY, lAx, lAy, lVx, lVy;
synchronized (LOCK) {
lWidth = pixelWidth;
lHeight = pixelHeight;
lBallX = ballPixelX;
lBallY = ballPixelY;
lVx = velocityX;
lVy = velocityY;
lAx = accelX;
lAy = -accelY;
}
if (lWidth <= 0 || lHeight <= 0) {
// invalid width and height, nothing to do until the GUI comes up
return;
}
long curTime = System.currentTimeMillis();
if (lastTimeMs < 0) {
lastTimeMs = curTime;
return;
}
long elapsedMs = curTime - lastTimeMs;
lastTimeMs = curTime;
// update the velocity
// (divide by 1000 to convert ms to seconds)
// end result is meters / second
lVx += ((elapsedMs * lAx) / 1000) * pixelsPerMeter;
lVy += ((elapsedMs * lAy) / 1000) * pixelsPerMeter;
// update the position
// (velocity is meters/sec, so divide by 1000 again)
lBallX += ((lVx * elapsedMs) / 1000) * pixelsPerMeter;
lBallY += ((lVy * elapsedMs) / 1000) * pixelsPerMeter;
boolean bouncedX = false;
boolean bouncedY = false;
if (lBallY - ballRadius < 0) {
lBallY = ballRadius;
lVy = -lVy * rebound;
bouncedY = true;
} else if (lBallY + ballRadius > lHeight) {
lBallY = lHeight - ballRadius;
lVy = -lVy * rebound;
bouncedY = true;
}
if (bouncedY && Math.abs(lVy) < STOP_BOUNCING_VELOCITY) {
lVy = 0;
bouncedY = false;
}
if (lBallX - ballRadius < 0) {
lBallX = ballRadius;
lVx = -lVx * rebound;
bouncedX = true;
} else if (lBallX + ballRadius > lWidth) {
lBallX = lWidth - ballRadius;
lVx = -lVx * rebound;
bouncedX = true;
}
if (bouncedX && Math.abs(lVx) < STOP_BOUNCING_VELOCITY) {
lVx = 0;
bouncedX = false;
}
// safely copy local vars back to object fields
synchronized (LOCK) {
ballPixelX = lBallX;
ballPixelY = lBallY;
velocityX = lVx;
velocityY = lVy;
}
if (bouncedX || bouncedY) {
Vibrator v = vibratorRef.get();
if (v != null) {
v.vibrate(20L);
}
}
}
public void setVibrator(Vibrator v) {
vibratorRef.set(v);
}
}
Logcat error:
FATAL EXCEPTION: main
java.lang.RuntimeException: Unable to start activity ComponentInfo{com.stuffthathappens.games/com.stuffthathappens.games.BouncingBallActivity}: java.lang.NullPointerException
at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2194)
at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2229)
at android.app.ActivityThread.access$600(ActivityThread.java:139)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1261)
at android.os.Handler.dispatchMessage(Handler.java:99)
at android.os.Looper.loop(Looper.java:154)
at android.app.ActivityThread.main(ActivityThread.java:4945)
at java.lang.reflect.Method.invokeNative(Native Method)
at java.lang.reflect.Method.invoke(Method.java:511)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:784)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:551)
at dalvik.system.NativeStart.main(Native Method)
Caused by: java.lang.NullPointerException
at com.stuffthathappens.games.BouncingBallActivity.onCreate(BouncingBallActivity.java:52)
at android.app.Activity.performCreate(Activity.java:4531)
at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1071)
at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2150)
You can declare you array like this:
private BouncingBallModel[] model = new BouncingBallModel[50];
like the following
How do I declare and initialize an array in Java?
guys. I'm playing around with making my very first Android game, but stumbled into a problem. The framerate seems to have random lag spikes. If I comment the background(s) out the framerate gets much smoother. I've looked around SO and can't find anything to solve my problems. I have a feeling it has something to do with allocating a specific amount of time every time I draw, but I don't know how to properly implement such a feature. Any suggestions? Btw, tryed hardware ac, anti etc.
This is the class that starts the surfaceview :
package com.example.glassrunner;
Imports Here
public class Game extends Activity
{
MySurfaceView mySurfaceView;
public SoundPool spool;
private int soundID;
int length=0;
/** Called when the activity is first created. */
#Override
public void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
requestWindowFeature(Window.FEATURE_NO_TITLE);
this.setVolumeControlStream(AudioManager.STREAM_MUSIC);
mySurfaceView = new MySurfaceView(this);
setContentView(mySurfaceView);
}
#Override
protected void onResume()
{
// TODO Auto-generated method stub
super.onResume();
mySurfaceView.onResumeMySurfaceView();
}
#Override
protected void onPause()
{
// TODO Auto-generated method stub
super.onPause();
mySurfaceView.onPauseMySurfaceView();
}
#Override
protected void onDestroy()
{
super.onDestroy();
mySurfaceView = null;
}
}
This is the surfaceview class :
package com.example.glassrunner;
Imports here
public class MySurfaceView extends SurfaceView implements Runnable
{
public static boolean gameOver = false;
SurfaceHolder surfaceHolder;
Thread thread = null;
public Integer score=0;
public SoundPool spool;
private int soundID;
int length=0;
public static MediaPlayer mp;
volatile boolean running = false;
int Yposition = 450;
int Xposition = 50;
Paint textPaint;
long mLastTime;
Bitmap background;
Bitmap background2;
Bitmap lines;
Bitmap runSprite;
Bitmap box;
Paint bitmapPaint ;
Paint textPaint2;
Bitmap scaledBackground ;
Bitmap scaledBackground2 ;
Bitmap scaledLines ;
Bitmap scaledBox;
Canvas canvas;
Paint paint;
int SpX=0;
int SpY=0;
Bitmap[][] sprite;
/** Variables for the counter */
int frameSamplesCollected = 0;
int frameSampleTime = 0;
int fps = 0;
int speed = 5;
Toast GameOverToast;
Context context;
MediaPlayer mMediaPlayer;
public MySurfaceView(Context context)
{
super(context);
this.context = context;
// TODO Auto-generated constructor stub
surfaceHolder = getHolder();
surfaceHolder.setFormat(PixelFormat.RGB_565);
CharSequence text = "Game Over!";
int duration = Toast.LENGTH_SHORT;
GameOverToast = Toast.makeText(context, text, duration);
spool = new SoundPool(10, AudioManager.STREAM_MUSIC, 0);
soundID = spool.load(context, R.raw.jump, 1);
mp = MediaPlayer.create(context, R.raw.saturdaymorningfunk);
initialization();
}
public void initialization()
{
mp.setLooping(true);
mp.start();
Options options = new Options();
options.inSampleSize = 1/4;
options.inPreferredConfig = Bitmap.Config.RGB_565;
background=BitmapFactory.decodeResource(getResources(),R.drawable.background,options);
lines=BitmapFactory.decodeResource(getResources(),R.drawable.lines);// getting the png from drawable folder
background2=BitmapFactory.decodeResource(getResources(),R.drawable.background2,options);
runSprite=BitmapFactory.decodeResource(getResources(),R.drawable.runsprite);
box=BitmapFactory.decodeResource(getResources(),R.drawable.box);
bitmapPaint = new Paint(Paint.ANTI_ALIAS_FLAG); // tool for painting on the canvas
bitmapPaint.setAntiAlias(true);
bitmapPaint.setFilterBitmap(true);
textPaint = new Paint();
textPaint.setColor(Color.RED);
textPaint.setTextSize(32);
textPaint2 = new Paint();
textPaint2.setColor(Color.BLUE);
textPaint2.setTextSize(50);
scaledBackground = Bitmap.createScaledBitmap(background, 2560, 500, true);
scaledBackground2 = Bitmap.createScaledBitmap(background2, 2560, 400, true);
scaledLines = Bitmap.createScaledBitmap(lines, 2560, 30, true);
runSprite = Bitmap.createScaledBitmap(runSprite, 1400, 1000, true);
scaledBox = Bitmap.createScaledBitmap(box, 100, 100, true);
sprite = new Bitmap[4][7];
for(int row=0;row<=3;row++)
{
for(int col=0;col<=6;col++)
{
sprite[row][col] = Bitmap.createBitmap(runSprite, SpX, SpY, 200, 250);
SpX+=200;
}
SpX=0;
SpY+=250;
}
}
public void onResumeMySurfaceView()
{
mp.seekTo(length);
mp.start();
running = true;
thread = new Thread(this);
thread.start();
}
public void onPauseMySurfaceView()
{
mp.pause();
length=mp.getCurrentPosition();
boolean retry = true;
running = false;
while(retry){
try {
thread.join();
retry = false;
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
public void onDestroyMySurfaceView()
{
mp.stop();
running = false;
thread = null;
thread.stop();
}
private void fps()
{
long now = System.currentTimeMillis();
if (mLastTime != 0)
{
//Time difference between now and last time we were here
int time = (int) (now - mLastTime);
frameSampleTime += time;
frameSamplesCollected++;
//After 10 frames
if (frameSamplesCollected == 10)
{
//Update the fps variable
fps = (int) (10000 / frameSampleTime);
//Reset the sampletime + frames collected
frameSampleTime = 0;
frameSamplesCollected = 0;
}
}
mLastTime = now;
}
public boolean pressDown = false;
public long pressTime;
public boolean onTouchEvent(MotionEvent event)
{
if (event != null)
{
if (event.getAction() == MotionEvent.ACTION_DOWN)
{ if(Yposition == orgPos)
{
spool.play(soundID, 15, 15, 1, 0, 1f);
pressDown = true;
pressTime = System.currentTimeMillis();
}
}else if (event.getAction() == MotionEvent.ACTION_UP)
{
pressDown = false;
}
}
return true;
}
int x=0;
int y=100;
int x2=0;
int y2=20;
int row=0;
int col=0;
int limit = 100;
int orgPos = 450;
int Xbox = 1280;
int Ybox = 580;
Random r = new Random();
int RBox;
public static String Fscore;
boolean onTop = false;
long now;
long start;
long stop;
long time ;
int spritePosition = 0 ;
int spriteSize;
#Override
public void run()
{
while(running)
{
canvas = null;
if(surfaceHolder.getSurface().isValid())
{
canvas = surfaceHolder.lockCanvas();
fps(); // fps
// Update screen parameters
update();
draw();
surfaceHolder.unlockCanvasAndPost(canvas);
}
}
}
public void update()
{
if(score<500)
{
speed = 7;
}
else if(score%500 == 0)
{
speed = 7 + (score / 500);
}
if(col==6)
{
row++;
col=0;
}
if(row==4)
{
row=0;
}
score++;
Fscore = score.toString();
if(x>-1280)
{
x-=speed;
}else if(x<=-1280)
{
x=0;
}
if(x2>-1280)
{
x2-=5;
}else if(x2<=-1280)
{
x2=-0;
}
RBox = r.nextInt(999)+1280;
if(Xbox > -100)
{
Xbox-=speed;
}else if(Xbox<=-100)
{
Xbox=RBox;
}
if( (Xposition + 200 == Xbox +40 )&&(Yposition + 250 > Ybox+20)||( Xposition+200<=Xbox+70)&&( Xposition+200>=Xbox+20)&&(Yposition + 250 > Ybox+30) ) // collision
{
GameOverToast.show();
running = false;
spool.release();
mp.release();
Looper.prepare();
Intent database = new Intent(context, MainHighscore.class);
database.putExtra("score", Fscore);
context.startActivity(database);
onDestroyMySurfaceView();
}
now = System.currentTimeMillis();
if(( now - pressTime) <= 600)
{
if(Yposition > limit)
{
Yposition -= 10;
}
}
onTop = false;
if((now - pressTime) >= 600 && (now - pressTime) <= 1200)
{
if(!(Yposition == orgPos))
{
if(Yposition+250 >= Ybox && Xposition+200>=Xbox+70 && Xposition <= Xbox+40)
{
onTop=true;
Yposition = 340;
}else
{
Yposition += 10;
}
}
}
if((now - pressTime) >= 1200)
{
if(Yposition < 450) Yposition +=10;
else Yposition = 450;
}
}
public void draw()
{
canvas.drawColor(Color.WHITE);
//canvas.drawBitmap(scaledBackground, x2,y2, bitmapPaint);
canvas.drawBitmap(scaledBackground2, x,y, bitmapPaint);
canvas.drawBitmap(scaledLines, x,650, bitmapPaint);
canvas.drawText(Fscore, 1050, 50, textPaint2);
canvas.drawText(fps + " fps", getWidth() / 2, getHeight() / 2, textPaint);
canvas.drawBitmap(sprite[row][col],Xposition,Yposition,bitmapPaint );
canvas.drawBitmap(scaledBox,Xbox,Ybox,bitmapPaint);
col++;
}
}
I think your problem might be actually the moving part. Your just drawing too much stuff, and the surfaceView is not meant for that.