I am creating a game using Andengine and in this I want an object flying in the sky to drop bombs below. Currently I am using a timehandler to create both the flying object and the bombs. But its not working properly. The problems I am facing are:
1) Sometimes instead of a single bomb 2 or 3 bombs are dropped.
2) And the bomb objects are also not getting recycled. The code for recycling the object is getting called, but it doesn't seem to work.
I have noticed one thing i.e. suppose, initially I have one flying object that drops a bomb. When the bomb leaves the screen, the recycling code for bomb object gets called once. And after sometime as the flying object leaves the screen it also gets recycled. Now when another flying object is created and it drops the bomb, the code for recycling the bomb is getting called twice. Then for third flying object, the code for recycling the single bomb is getting called thrice and so on.
The code to add flying object is as follows
private void createDragonHandler() {
TimerHandler spriteTimerHandler;
float mEffectSpawnDelay = 10f;
spriteTimerHandler = new TimerHandler(mEffectSpawnDelay, true,
new ITimerCallback() {
#Override
public void onTimePassed(TimerHandler pTimerHandler) {
addDragon();
}
});
getEngine().registerUpdateHandler(spriteTimerHandler);
}
public void addDragon() {
Dragon dragon = (Dragon) dragonSpritePool.obtainPoolItem();
if (dragon.getParent() != mainScene)
mainScene.attachChild(dragon);
}
public synchronized Dragon obtainPoolItem() {
dragon = super.obtainPoolItem();
Random rand = new Random();
final int x = (int) (CAMERA_WIDTH + resourceManager.dragonTextureRegion
.getWidth());
int minY = (int) resourceManager.dragonTextureRegion.getHeight();
int maxY = (int) (CAMERA_HEIGHT / 2 - resourceManager.dragonTextureRegion
.getHeight());
int rangeY = maxY - minY;
final int y = rand.nextInt(rangeY) + minY;
dragon.reset();
dragon.setVisible(true);
dragon.setIgnoreUpdate(false);
dragon.animate(150);
dragon.setPosition(x, y);
MoveXModifier mod = new MoveXModifier(15, dragon.getX(),
-dragon.getWidth());
float mEffectSpawnDelay = 5f;
TimerHandler spriteTimerHandler = new TimerHandler(mEffectSpawnDelay,
true, new ITimerCallback() {
#Override
public void onTimePassed(TimerHandler pTimerHandler) {
DragonFire dragonFire = BirdShoot.dragonFireSpritePool
.obtainPoolItem(dragon.getX(), dragon.getY());
BirdShoot.mainScene.attachChild(dragonFire);
}
});
dragon.registerEntityModifier(mod.deepCopy());
dragon.setNoHit(0);
PhysicsHandler mPhysicsHandler;
mPhysicsHandler = new PhysicsHandler(dragon);
dragon.setPhysicsHandler(mPhysicsHandler);
dragon.registerUpdateHandler(dragon.getmPhysicsHandler());
BirdShoot.engine.registerUpdateHandler(spriteTimerHandler);
return dragon;
}
`
Where are you writing the code for recycling the sprite objects? According to your problem, I see something fishy there. It might be possible that the code is not getting executed. The code to recycle the sprites must be something like this:-
public void onHandleRecycleItem(Sprite sprite){
super.onHandleRecycleItem(sprite);
// Your code to recycle the object
}
Also it should be called using the following line :
yourPool.recyclePoolItem(spriteObject);
Without seeing your code, it will be hard to help with this. But, this has the smell of things not being done on the updateThread.
Any time you do something that affects AndEngine, be sure you call it via
runOnUpdateThread(new Runnable() {
#Override
public void run() {
/* Do your AndEngine changes in here*/
}
});`
Related
I'm in the process of writing my first android app, an asteroids clone, and I've got one last bug I can't figure out. The game activity will occasionally freeze when the game ends and tries to go to the game over activity. The game activity has a few UI views that stop working before it freezes. There is a Button to pause the game which stops responding to input. There is also a TextView for the score that I am updating using runOnUIThread() (since I am running the game on a separate thread) which stops updating.
There are a few things that make this puzzling to me. First off, I'm not seeing any error messages. The bug only happens about 1 out of every 5 times the game activity is started and it happens at random points while it is running. It happens when the game activity starts about half the time, but seemingly may occur at any point, although it does tend to happen sooner than later even when it doesn't occur right at the start. Also, the game thread and the custom game View I made keep updating fine when the bug occurs. Finally, I have two virtual joystick Views I made to control the player's motion and shooting which also keep updating and responding to touch input fine. These last two really confuse me because at first I thought the main thread was doing too much work or getting stuck in a loop or something, but if I understand correctly (and according to some tests I've done) drawing and touch input happen on the main thread, and as far as I can tell all views are drawing fine and touch input is working for the joysticks but not the pause button. Also, if the main thread was unresponsive, wouldn't I get an app not responsive popup? That never happens though.
I've tried removing all instances of runOnUIThread() just to see if that fixed it with no effect other than breaking the score TextView and leaving the app stuck in the game activity even when the bug doesn't occur. I've tried printing the names of the threads returned by Thread.currentThread() in the game loop, in the onDraw() method of the game view, in the onTouch() method of JoystickView and when updating the score TextView in the game activity to ensure I actually am running 2 separate threads and everything is running on the threads they should be (game thread for the game loop, main thread for everything else), and that checked out. I've tried cutting down on creating new objects during the game loop, not sure what that would do other than help performance to be honest but I figured it couldn't hurt, also to no effect.
This all makes it difficult for me to know what code to post. Here are some guesses:
The onCreate, goToGameOverActivity, and updateScoreTextView methods from my game activity:
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN);
setVolumeControlStream(AudioManager.STREAM_MUSIC);
setContentView(R.layout.activity_play);
gameView = findViewById(R.id.gameView);
game = new Game(this, gameView);
float joystickRadius = 150;
CartesianPoint2D motionJoystickCenter = new CartesianPoint2D(
10 + joystickRadius,
Constants.getScreenInformation().getHeight() - 10 - joystickRadius);
motionJoyStickView = new MotionJoystickView(this, joystickRadius, motionJoystickCenter, game);
CartesianPoint2D shootJoystickCenter = new CartesianPoint2D(
Constants.getScreenInformation().getWidth() - 10 - joystickRadius,
Constants.getScreenInformation().getHeight() - 10 - joystickRadius);
shootJoystickView = new ShootJoystickView(this, joystickRadius, shootJoystickCenter, game);
scoreTextView = findViewById(R.id.playActivityScoreTextView);
pauseButton = findViewById(R.id.playActivityPauseButton);
soundEffects = new SoundEffects(this);
game.start();
}
public void goToGameOverActivity() {
game.stop();
Intent intent = new Intent(this, GameOverActivity.class);
startActivity(intent);
finish();
}
public void updateScoreTextView(int score) {
scoreTextView.setText(String.valueOf(score));
scoreTextView.invalidate();
}
The Runnables I am using to update the score TextView and signal that the game is over using runOnUIThread():
package com.group18.spacerocks.play.view;
import com.group18.spacerocks.play.PlayActivity;
public class UpdateScoreTextView implements Runnable {
protected int newScore;
protected PlayActivity playActivity;
public UpdateScoreTextView(int newScore, PlayActivity playActivity) {
System.out.println("Setting ScoreTextView to " + newScore + ".");
this.newScore = newScore;
this.playActivity = playActivity;
}
#Override
public void run() {
playActivity.updateScoreTextView(newScore);
}
}
package com.group18.spacerocks.play;
public class GoToGameOverActivity implements Runnable {
protected PlayActivity playActivity;
public GoToGameOverActivity(PlayActivity playActivity) {
this.playActivity = playActivity;
}
#Override
public void run() {
playActivity.goToGameOverActivity();
}
}
The run() method from the GameThread class extending thread which updates the game:
public void run() {
super.run();
while (running) {
while (paused) {
currentTime = System.nanoTime();
lastUpdateTime = currentTime;
try {
sleep(1);
}
catch (InterruptedException e) {
e.printStackTrace()
}
}
currentTime = System.nanoTime();
timeSinceLastUpdate = currentTime - lastUpdateTime;
if (timeSinceLastUpdate >= TARGET_UPDATE_LENGTH) {
currentUpdateLength = currentTime - lastUpdateTime;
game.update(currentUpdateLength);
gameView.invalidate();
lastUpdateTime = currentTime;
UPS++;
timeSinceLastUpdate = 0;
if (currentTime - lastSecondTime >= 1000000000) {
System.out.println("UPS: " + UPS);
UPS = 0;
lastSecondTime = currentTime;
}
}
}
}
And finally the code for my JoystickView class, which has 2 child classes for motion and shooting:
package com.group18.spacerocks.play.joystick;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
import com.group18.spacerocks.R;
import com.group18.spacerocks.play.CartesianPoint2D;
import com.group18.spacerocks.play.PlayActivity;
import com.group18.spacerocks.play.Game;
public class JoystickView extends View implements View.OnTouchListener {
protected boolean inUse;
protected float radius;
protected float buttonRadius;
protected CartesianPoint2D center;
protected CartesianPoint2D buttonOffset;
protected PlayActivity playActivity;
protected Game game;
protected Paint outlinePaint;
protected Paint buttonPaint;
public JoystickView(PlayActivity playActivity, float radius, CartesianPoint2D center, Game game) {
super(playActivity);
inUse = false;
this.radius = radius;
buttonRadius = radius / 2;
this.center = center;
this.buttonOffset = CartesianPoint2D.getInstance();
this.playActivity = playActivity;
this.game = game;
setLayoutParams(new ViewGroup.LayoutParams((int) radius * 2, (int) radius * 2));
setX(center.getX() - radius);
setY(center.getY() - radius);
outlinePaint = new Paint(Paint.ANTI_ALIAS_FLAG);
outlinePaint.setAlpha(100);
outlinePaint.setStyle(Paint.Style.STROKE);
outlinePaint.setColor(Color.WHITE);
outlinePaint.setStrokeWidth(1);
buttonPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
buttonPaint.setAlpha(100);
buttonPaint.setStyle(Paint.Style.STROKE);
buttonPaint.setColor(playActivity.getResources().getColor(R.color.textTitleColor));
buttonPaint.setStrokeWidth(10);
setOnTouchListener(this);
ViewGroup viewGroup = playActivity.findViewById(R.id.playActivityLayout);
viewGroup.addView(this);
}
public void onDraw(Canvas canvas) {
canvas.drawCircle(radius, radius, radius, outlinePaint);
canvas.drawCircle(radius + buttonOffset.getX(), radius + buttonOffset.getY(), buttonRadius, buttonPaint);
}
public boolean onTouch(View view, MotionEvent motionEvent) {
if (motionEvent.getAction() == MotionEvent.ACTION_UP) {
inUse = false;
buttonOffset.setX(0);
buttonOffset.setY(0);
}
else {
inUse = true;
CartesianPoint2D motionPosition = new CartesianPoint2D(motionEvent.getX(), motionEvent.getY());
CartesianPoint2D centerOffset = new CartesianPoint2D(motionPosition.getX() - radius, motionPosition.getY() - radius);
if (centerOffset.getMagnitude() > radius) {
buttonOffset = CartesianPoint2D.createFromMagnitudeAndDirection(radius, centerOffset.getDirection());
} else {
buttonOffset = centerOffset;
}
}
invalidate();
return true;
}
}
Any insight is appreciated, I've been trying to fix this for a few days now and I'm at a loss.
Still don't know exactly what the cause was, but I fixed it by making the game view an extension of SurfaceView so I could draw in a thread other than the main thread.
I'm writing a simple Whack a Mole clone, and I've got my UI elements declared in a GridLayout in a layout.xml, then assigned to ImageView variables in an array programmatically. I've got a startGame() method that simply takes a random int, pulls it from the array and causes it to go visible for a second, then repeats. For some reason, when I put this code in a while() loop, it causes my UI to go blank as soon as it's launched.
I know it's the while() loop because I tried taking the code out of the while() loop, and it ran correctly (once), but turns everything white when placed in a while loop.
Here's the method causing the problem:
public void startGame() {
gameStarted = true;
while(gameStarted) {
randomInt = rand.nextInt(11);
mole[randomInt].setVisibility(View.VISIBLE);
handler.postDelayed(new Runnable() {
#Override
public void run() {
mole[randomInt].setVisibility(View.INVISIBLE);
}
}, 5000);
}
}
All the other relevant code is in onCreate, it's otherwise just a skeleton Activity subclass.
public class WAM_Activity extends Activity {
private ImageView[] mole = new ImageView[11];
private int[] moleId = {R.id.mole1, R.id.mole3, R.id.mole4, R.id.mole5, R.id.mole6, R.id.mole7, R.id.mole8, R.id.mole9, R.id.mole10, R.id.mole11, R.id.mole12};
private boolean gameStarted;
private int randomInt = 0;
private Random rand = new Random();
Handler handler = new Handler();
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.wam_view_layout);
for (int i = 0; i < 11; i++) {
mole[i] = (ImageView) findViewById(moleId[i]);
mole[i].setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
//do stuff eventually
}
});
}
gameStarted = true;
startGame();
}
Any idea why this isn't working? I've been staring at it for hours and I'm quite stumped.
Android doesn't work that way, when onCreate is called, it need to be finished in order for the app to keep responding, I'm surprised you are not getting any "App not respopnding" error.
If you want to create a "game loop" you can simply by creating a new Thread and putting the while in there.
Activity's lifecycle must be executed without blocking them for the app to operate correctly, for more info check here.
Do you know about threads? if you want i can post an example of how to do that with threads but it might be long and if you don't know what a Thread is it will be too confusing for you.
Edit: Ok I'll make an example of a Thread
When I create my games I usually have only one Activity that the only thing it does is creating a custom SurfaceView and nothing else.
public class GameActivity extends Activity
{
//This is a custom class that extends SurfaceView - I will write it below
private GameSurface game;
#Override
protected void onCreate(Bundle bundle)
{
super.onCreate(bundle);
//Create a new instance of the game
game = new GameSurface(this);
//Set the View of the game to the activity
setContentView(game);
}
}
You can also add extra stuff like onSaveInstanceState to save game data and restore them later but I don't want to add them now so the code looks simple.
This class was very simple, let's move on to our SurfaceView. The reason I picked a SurfaceView to do that it's because it is made to allow custom graphics to be drawn on it - exactly what we want on a video game. I will try to make the class as simple as possible:
/*SurfaceHolder.Callback will run some functions in our class when
our surface is completed - at that point we can initialize data
that have to do with the View's width/height.
I don't know if you've noticed that on a View's onCreate()
when you call getWidth() or getHeight() you get 0, that's because
the surface is not initialized yet, this is a way to fix that.
Also we need a Runnable to run the Thread inside this class,
no need to make more classes and make it more complicated*/
public class GameSurface extends SurfaceView
implements SurfaceHolder.Callback, Runnable
{
//This is our thread - we need the "running" variable to be
//able to stop the Thread manually, this will go inside our "while" loop
private Thread thread;
private boolean running;
//Right here you can add more variables that draw graphics
//For example you can create a new class that has a function that
//takes Canvas as a parameter and draws stuff into it, I will add
//a Rect in this case which is a class already made by android
//but you can create your own class that draws images or more
//complicated stuff
private Rect myRect;
//Rect needs a paint to give it color
private Paint myPaint;
//Constructor
public GameSurface(Context context)
{
super(context);
//This is the callback to let us know when surface is completed
getHolder().addCallback(this);
}
//When a class implements SurfaceHolder.Callback you are forced to
//create three functions "surfaceCreated", "surfaceChanged" and
//"surfaceDestroyed" these are called when the surface is created,
//when some settings are changed (like the orientation) and when
//it is about to be destroyed
#Override
public void surfaceCreated(Surface holder)
{
//Let's initialize our Rect, lets assume we want it to have 40
//pixels height and fill the screen's width
myRect = new Rect(0, 0, getWidth(), 40);
//Give color to the rect
myPaint = new Paint();
myPaint.setARGB(0, 255, 0, 0);
//In case you are not familiar with the Rect class, as
//parameters it gets Rect(left, top, right, bottom)
//Time to start our Thread - nothing much to explain here if
//you know how threads work, remember this class implements
//Runnable so the Thread's constructor gets "this" as parameter
running = true;
thread = new Thread(this);
thread.start();
}
//We won't use this one for now, but we are forced to type it
//Even if we leave it empty
#Override
public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {}
//When the surface is destroyed we just want the Thread to
//terminate - we don't want threads running when our app is not visible!
#Override
public void surfaceDestroyed(SurfaceHolder holder)
//We will type this function later
{destroyThread();}
//Time for the interesting stuff! let's start with input
#Override
public boolean onTouchEvent(MotionEvent event)
{
//The logic is as follows: when our Rect is touched, we want
//it to become smaller
if (event.getAction() == MotionEvent.ACTION_DOWN)
{
if (myRect.contains((int) event.getX(), (int) event.getY())
{
myRect.right -= 5;
//Return true - we did something with the input
return true;
}
}
return super.onTouchEvent(event);
}
//This is our update, it will run once per frame
private void update()
{
//Let's assume we want our rect to move 1 pixel downwards
//on every frame
myRect.offset(0, 1);
}
//Now for our draw function
public void draw(Canvas canvas)
{
//Here we want to draw a background and our rect
canvas.drawARGB(0, 0, 0, 255);
canvas.drawRect(myRect, myPaint);
}
//The only thing left is our run() function for the Thread
#Override
public void run()
{
//Screen
Canvas canvas;
//Our game cycle (the famous while)
while(running)
{
//Count start time so we can calculate frames
int startTime = System.currentTimeMillis();
//Update our game
update();
//Empty screen so it can obtain new instance
canvas = null;
//Try locking the canvas for pixel editing on surface
try
{
//Try getting screen
canvas = getHolder().lockCanvas();
//Succeeded
if (canvas != null) synchronized (getHolder())
{
//Actual drawing - our draw function
draw(canvas);
}
} finally
{
//Draw changes
if (canvas != null) getHolder().unlockCanvasAndPost(canvas);
}
//End Frame - 1000/30 means 30 frames per second
int frameTime = System.currentTimeMillis() -startTime;
if (frameTime < 1000/30)
try { Thread.sleep(1000/30 -frameTime); } catch (InterruptedException e){}
}
}
//Last but not least, our function for closing the thread
private void destroyThread()
{
//Stop thread's loop
running = false;
//Try to join thread with UI thread
boolean retry = true;
while (retry)
{
try {thread.join(); retry = false;}
catch (InterruptedException e) {}
}
}
}
I may have made some minor mistakes (probably with case sensitive letters) so feel free to correct these, I wrote the code at once so I didn't have time to test it, it should work flawlessly though.
If you have any more questions, need more explanation or something is not working right let me know!
I am quite new to Android and want to create a simple game.
Therefor i need a thread which is drawing a transparent Rectangle on different positions every 4 seconds with a 2 second break (without drawing).
I got it working with a "recursive" thread calling a new instance of itself with handler.postdelayed.
My feeling about threads tells me, that this isn't a very nice way...
While searching here for similar topics, i found out about the Timer construct. Can i use this for my problem?
Is there a better way to do this?
(edit) The thread is meant for highlighting part of the gameboard, but only for 4 seconds. After that there should be 2 seconds without highlighting. Then 4 seconds highlighting the next part of the board etc.
(edit2) I couldn't use sleep, because it froze my UI. If anyone has a similar situation, here is how i solved it:
public class myRunnable implements Runnable {
private int duration;
private int counter;
private boolean highlight;
public myRunnable(int duration, boolean highlight) {
this.duration = duration;
this.highlight = !highlight;
}
#Override
public void run() {
if (highlight) {
// highlight 4s long
highlight();
invalidate();
myThread = new myRunnable(duration, highlight);
postDelayed(myThread, duration);
} else {
// pause (2s)
resetHighlight();
invalidate();
myThread = new myRunnable(duration, highlight);
postDelayed(myThread, noHighlightDuration);
}
}
}
The Best way to do this is use to use invalidate() to call the onDraw() function and a method to update the position. Something like this-
int x,y;
protected void onDraw(Canvas canvas) {
x=10;
y=10;
canvas.drawRect(___);
update();
invalidate();
}
private void update()
{/*Change the x and y/*}
Here everytime inavlidate() is called from anywhere the onDraw() is called again with the new x and y.
I was trying to make the enemies in my game go up and down; since I'm using a physic body with a Sprite on it I can't use entity modifiers, so I decided give the body a little push using the .setLinearVelocity(float x, float y) method every time its sprite reach a certain point in the screen.
With just one body works great, but I need to have other enemeis (same sprite, different body) spawning every 5 seconds and doing the same thing, but I don't know how to track them... I mean, I don't know how to control if each body reaches the Y location independently from one another...
For example, right now the code is like this:
private void add_Box_Face()
{
float random_x = (float) (28 + (int)(Math.random() * ((this.CAMERA_WIDTH - 28*2) + 1)));
final Body rectangle_face_body;
final Sprite rectangle_face = new Sprite(random_x, this.y, this.mRectangleFaceTextureRegion, this.getVertexBufferObjectManager());
rectangle_face_body = PhysicsFactory.createBoxBody(this.m_PhysicsWorld, rectangle_face, BodyType.DynamicBody, this.BOX_FIXTURE_DEF);
rectangle_face_body.setUserData("target");
//I give the body a initial push
rectangle_face_body.setLinearVelocity(0, -5);
//I register an update handler to the sprite to control if it reaches a certain Y value
rectangle_face.registerUpdateHandler(new IUpdateHandler()
{
#Override
public void onUpdate(float pSecondsElapsed)
{
if (rectangle_face.getY() >= y-50)
{
//Here I just use a flag so that later on below I can do the push
MyApp.this.setLinearVelocity = true;
}
}
#Override
public void reset()
{
// TODO Auto-generated method stub
}
});
//Here I register the physic connector and if the flag permits it, I push the body up
this.m_PhysicsWorld.registerPhysicsConnector(new PhysicsConnector(rectangle_face, rectangle_face_body, true, false)
{
#Override
public void onUpdate(float pSecondsElapsed)
{
super.onUpdate(pSecondsElapsed);
if(MyApp.this.setLinearVelocity)
{
rectangle_face_body.setLinearVelocity(0, -3);
MyApp.this.setLinearVelocity = false;
}
}
});
this.mscene.attachChild(rectangle_face);
}
With the code like this the first body do what is planned, it moves up and down but as soon as another body pops up, it falls down and the other body goes up because the boolean setLinearVelocity is always set to true, so there is a costant push upwards; when a third body comes in, the second body falls down as well and this last one takes its place going up
With this code I didn't expect much else... but I don't know what else I can try... how can I control this?
Thanks in advance :)
EDIT: Added working code in an anwser below
I suggest you separate the code for the enemy from the code for the update handler. Create a class Enemy that will contain the Sprite and Body, hold your Enemies in an array and override the onUpdate method of your PhysicsWorld so that it goes through the array of Enemies and does what you want to all of them.
Here's a code snippet showing a very simple way of doing this:
mEngine.registerUpdateHandler(new IUpdateHandler() {
#Override
public void reset() {}
#Override
public void onUpdate(float pSecondsElapsed) {
for (Enemy e : enemies) {
e.checkPositionAndBounce();
}
}
});
Please note that this may not a very good idea as this code will probably run on a thread different to the one of the physics engine, which could cause all sorts of problems. A better way would be overriding the onUpdate method of PhysicsWorld:
#Override
public void onUpdate(final float pSecondsElapsed) {
super.onUpdate();
for (Enemy e : enemies) {
e.checkPositionAndBounce();
}
}
If you are unsure of what the first snippet means, look up "anonymous inner class".
Ok, here's the final working code (I didn't create a method to check the position or a class for the enemies just because right now I'm just messing around with the mechanics; I'll create a new project when I'm ready to really start):
this.m_PhysicsWorld = new PhysicsWorld(new Vector2(0, SensorManager.GRAVITY_EARTH), false)
{
#Override
public void onUpdate(float pSecondsElapsed)
{
super.onUpdate(pSecondsElapsed);
for(int i = 0; i <= MyApp.this.mSpriteCounter; i++)
{
if (rectangle_face[i].getY() >= y-50)
{
final PhysicsConnector spritePhysicsConnector = m_PhysicsWorld.getPhysicsConnectorManager().findPhysicsConnectorByShape(rectangle_face[i]);
spritePhysicsConnector.getBody().setLinearVelocity(0, -3);
}
}
};
In this code rectangle_face[] is an array of Sprites; each sprite is created like this:
private void add_Box_Face()
{
float random_x = (float) (28 + (int)(Math.random() * ((this.CAMERA_WIDTH - 28*2) + 1)));
final Body rectangle_face_body;
rectangle_face[this.mSpriteCounter] = new Sprite(random_x, y, this.mRectangleFaceTextureRegion, this.getVertexBufferObjectManager());
rectangle_face_body = PhysicsFactory.createBoxBody(this.m_PhysicsWorld, rectangle_face[this.mSpriteCounter], BodyType.DynamicBody, this.BOX_FIXTURE_DEF);
rectangle_face_body.setUserData("target");
rectangle_face_body.setLinearVelocity(0, -5);
this.m_PhysicsWorld.registerPhysicsConnector(new PhysicsConnector(rectangle_face[this.mSpriteCounter], rectangle_face_body, true, false));
this.mscene.attachChild(rectangle_face[this.mSpriteCounter]);
}
That's it, thanks again for the help :)
To use a SurfaceView for drawing a 2D game in Android, I use this in the main activity's onCreate():
setContentView(new GameView(this));
Which is a reference to this class:
public class GameView extends SurfaceView implements SurfaceHolder.Callback
Additionally, I have a thread with its run() function:
public void run() {
Canvas c;
while (_run) {
c = null;
try {
c = _surfaceHolder.lockCanvas(null);
synchronized (_surfaceHolder) {
_panel.updatePhysics();
_panel.onDraw(c);
}
}
finally { // when exception is thrown above we may not leave the surface in an inconsistent state
if (c != null) {
_surfaceHolder.unlockCanvasAndPost(c);
}
}
}
}
In updatePhysics() I do some calculations. They are more complex than this simple example, of course, but work the same way:
public void updatePhysics() {
GraphicObject.Coordinates coord;
GraphicObject.Speed speed;
for (GraphicObject graphic : _allElements) {
coord = graphic.getCoordinates();
speed = graphic.getSpeed();
coord.setX(coord.getX() + speed.getX());
coord.setY(coord.getY() + speed.getY());
...
}
}
And in onDraw(), I draw everything to the canvas:
#Override
public void onDraw(Canvas canvas) {
canvas.drawBitmap(BITMAP, xPos, yPos, null);
...
}
This works fine - everything. And when I tested it on my device, it looked pretty good. But when I gave it to someone else and he did a test game, the objects were moving much faster! Why is this so? Because the thread calls updatePhysics() as often as possible which means that fast devices call this function more often?
How can I prevent this and make the game equally fast on all devices? Something like this?
private long lastRun = System.currentTimeMillis();
public void updatePhysics() {
long millisPassed = System.currentTimeMillis()-lastRun;
...
float newCoord = (coord.getX() + speed.getX()) * millisPassed / 33;
coord.setX(newCoord);
...
}
Thanks for your help!
If you can, use the time directly to calculate all your physics. That would usually work best.
If you have no way to calculate based on time because what you are doing that is just step based and you know that generating the next step does not take much time then you have another option.
You create two threads. The first one advances the state at a fixed rate (and you have to be sure that this works on slow devices at that rate too). The second one takes the current state is sees and draws that. Now the second thread can be as slow as it wants because it simply skips some states (or draws the same state several times if it is faster).
Small example below has one thread that advances some state object and replaces the reference each time so the consuming thread does not need to worry that it's state object gets modified
class GameState {
private int state = 0;
public GameState advanceState() {
GameState result = new GameState();
result.state = this.state + 1;
return result;
}
}
class SurfaceViewImplementation extends SurfaceView {
// the current state
volatile GameState mState = new GameState();
void somewhere() {
Thread fastProducer = new Thread(new Runnable() {
private static final long MAX_WAIT = 1000 / 60;
#Override
public void run() {
while (!Thread.interrupted()) {
long timeBefore = SystemClock.currentThreadTimeMillis();
GameState newState = mState.advanceState();
mState = newState;
long timeAfter = SystemClock.currentThreadTimeMillis();
long timeSpent = timeAfter - timeBefore;
SystemClock.sleep(Math.max(0, MAX_WAIT - timeSpent));
}
}
});
fastProducer.start();
Thread slowConsumer = new Thread(new Runnable() {
#Override
public void run() {
while (!Thread.interrupted()) {
GameState currentState = mState;
longRunningDraw(currentState);
}
}
});
slowConsumer.start();
}
}
That will still fail to give you a speed independant result if the producing thread can't run at the desired rate.
I would save the time when I start rendering the frame(in your case is updatePhysics()), and then next time I would got to this point, I know how much time pass, if it's to fast you can use Thread.Sleep();