I'm completely new to Android game development and trying to develop a game using AndEngine.
The first steps went fine I think, but now I'm a little bit stuck.
The final game should be something like Air Control (https://play.google.com/store/apps/details?id=dk.logisoft.aircontrol) (Except I'm not using aircrafts but my nephews in space ;))
What the createPlayer() method should do: It should create a player at a random offscreen position, get a random offscreen target position and draw a path between the two points. Now the player should fly using a PathModifier. Before the player is visible on the screen there should be a little arrow as indicator that shows where the next player is coming from. When the player appears on the screen this little indicator should disappear.
I'm spawning the players currently using a simple timer. And everything works as I want when I set the interval of the timer to like 10 secs.
When I set the timer to something smaller, like 1 sec the problem is, that the indicator does not get removed. It just stays at its last position.
I think it is because there is already a new player spawned and that player or the playerIndicator are pointing to the newly created ones. And I dont know how to change that.
Where is my mistake?
Here is my createPlayer method and the player class:
createPlayer() Method
private void createPlayer() {
float[] startPos = getPlayerRandomStartPosition();
float[] targetPos = getPlayerRandomTargetPosition(startPos);
int playerNr = 0 + (int)(Math.random() * ((2 - 0) + 1));
int[] playerSpeed = { 25, 35, 45 };
float[] playerScale = { 0.3f, 0.25f, 0.2f };
String[] playerName = { "marvin", "noah", "liam" };
ITiledTextureRegion[] tex = { resourcesManager.p1_region, resourcesManager.p2_region, resourcesManager.p3_region };
float pointsListX[] = { startPos[0], targetPos[0] };
float pointsListY[] = { startPos[1], targetPos[1] };
Path path = new Path(pointsListX.length);
for (int j = 0; j < pointsListX.length; j++) {
path.to(pointsListX[j], pointsListY[j]);
}
player = new Player(startPos[0], startPos[1], tex[playerNr], vbom) {};
player.registerUpdateHandler(new IUpdateHandler() {
#Override
public void reset() {}
#Override
public void onUpdate(float pSecondsElapsed) {
if (player.getPlayerIndicator() != null) {
if (player.getStartPos()[1] <= 0) {
player.getPlayerIndicator().setPosition(player.getX(), 0 + (resourcesManager.indicator_region.getHeight() / 2));
} else if (player.getStartPos()[1] >= 800) {
player.getPlayerIndicator().setPosition(player.getX(), 800 - (resourcesManager.indicator_region.getHeight() / 2));
} else if (player.getStartPos()[0] <= 0) {
player.getPlayerIndicator().setPosition(0 + (resourcesManager.indicator_region.getHeight() / 2), player.getY());
} else {
player.getPlayerIndicator().setPosition(480 - (resourcesManager.indicator_region.getHeight() / 2), player.getY());
}
}
if (camera.isEntityVisible(player)) {
if (player.getPlayerIndicator() != null) {
engine.runOnUpdateThread(new Runnable() {
#Override
public void run() {
final EngineLock engineLock = engine.getEngineLock();
engineLock.lock();
player.getPlayerIndicator().detachSelf();
player.getPlayerIndicator().dispose();
player.setPlayerIndicator(null);
engineLock.unlock();
}
});
}
}
}
});
player.registerEntityModifier(new PathModifier(playerSpeed[playerNr], path) {
#Override
protected void onModifierFinished(final IEntity pItem) {
engine.runOnUpdateThread(new Runnable() {
#Override
public void run() {
final EngineLock engineLock = engine.getEngineLock();
engineLock.lock();
pItem.detachSelf();
pItem.dispose();
engineLock.unlock();
addToScore();
}
});
super.onModifierFinished(pItem);
}
});
player.setHasBeenOnScreen(false);
player.setTargetPos(targetPos);
player.setStartPos(startPos);
player.setScale(playerScale[playerNr]);
player.setSpeed(playerSpeed[playerNr]);
player.setUserData(playerName[playerNr]);
player.setRotation(getTargetAngle(targetPos[0], targetPos[1], player.getX(), player.getY()));
PlayerIndicator playerIndicator;
if (startPos[1] <= 0) {
playerIndicator = new PlayerIndicator(startPos[0], 0 + (resourcesManager.indicator_region.getHeight() / 2), resourcesManager.indicator_region, vbom);
playerIndicator.setRotation(180);
} else if (startPos[1] >= 800) {
playerIndicator = new PlayerIndicator(startPos[0], 800 - (resourcesManager.indicator_region.getHeight() / 2), resourcesManager.indicator_region, vbom);
} else if (startPos[0] <= 0) {
playerIndicator = new PlayerIndicator(0 + (resourcesManager.indicator_region.getHeight() / 2), startPos[1], resourcesManager.indicator_region, vbom);
playerIndicator.setRotation(270);
} else {
playerIndicator = new PlayerIndicator(480 - (resourcesManager.indicator_region.getHeight() / 2), startPos[1], resourcesManager.indicator_region, vbom);
playerIndicator.setRotation(90);
}
playerIndicator.setAlpha(0.5f);
player.setPlayerIndicator(playerIndicator);
attachChild(playerIndicator);
attachChild(player);
}
Player class
public class Player extends AnimatedSprite {
public Player(float pX, float pY, ITiledTextureRegion pTiledTextureRegion, VertexBufferObjectManager pVertexBufferObjectManager) {
super(pX, pY, pTiledTextureRegion, pVertexBufferObjectManager);
long[] frameDurration = {300, 300, 300};
animate(frameDurration);
hasBeenOnScreen = false;
}
float[] startPos = { -100, -100 };
float[] targetPos;
int speed = 10;
PlayerIndicator playerIndicator;
boolean hasBeenOnScreen = false;
public PlayerIndicator getPlayerIndicator() {
return playerIndicator;
}
public void setPlayerIndicator(PlayerIndicator playerIndicator) {
this.playerIndicator = playerIndicator;
}
public boolean hasBeenOnScreen() {
return hasBeenOnScreen;
}
public void setHasBeenOnScreen(boolean hasBeenOnScreen) {
this.hasBeenOnScreen = hasBeenOnScreen;
}
public float[] getStartPos() {
return startPos;
}
public void setStartPos(float[] startPos) {
this.startPos = startPos;
}
public float[] getTargetPos() {
return targetPos;
}
public void setTargetPos(float[] targetPos) {
this.targetPos = targetPos;
}
public int getSpeed() {
return speed;
}
public void setSpeed(int speed) {
this.speed = speed;
}
}
Related
I am creating a mini game for Android, available from the playstore, which we call "Moon Buggy". The action is that you control a vehicle on the moon and you should defend yourself from attacking UFO:s.
Now I want to improve the movement of the vehicle. I have code so that it doesn't move outside the screen, and it is possible to accelerate, slow down and jump. But it is not perfect. I was inspired by the classic game Moon Patrol which is measuring time per completed section and my game does that too. So preferably you should be able to accelerate and complete the section faster the more you accelerate perhaps instead of stopping at the end of the screen which my vehicle does now. Also, perhaps it should accelerate faster.
The relevant code is:
ParallaxActivity.java
public class ParallaxActivity extends Activity implements View.OnClickListener {
long startTime;
long countUp;
TextView textGoesHere;
ParallaxView parallaxView;
#Override
public void onClick(View v) {
switch (v.getId()) {
case R.id.button3:
ParallaxView parallaxView = findViewById(R.id.backgroundImage);
parallaxView.recent = false;
parallaxView.brake = false;
parallaxView.buggyXDistance = parallaxView.buggyXDistance + 3;
parallaxView.distanceDelta = parallaxView.distanceDelta + 0.2;
break;
case R.id.button4:
ParallaxView parallaxView2 = findViewById(R.id.backgroundImage);
parallaxView2.recent = false;
parallaxView2.buggyXDistance = parallaxView2.buggyXDistance - 7;
parallaxView2.retardation = parallaxView2.retardation + 0.2;
parallaxView2.brake = true;
break;
case R.id.button2:
ParallaxView parallaxView3 = findViewById(R.id.backgroundImage);
parallaxView3.numberOfshots++;
parallaxView3.recent = false;
parallaxView3.launchMissile();
parallaxView3.scoring = true;
break;
case R.id.button1:
ParallaxView parallaxView4 = findViewById(R.id.backgroundImage);
if(parallaxView4.distanceDelta<3) parallaxView4.distanceDelta = parallaxView4.distanceDelta + 0.2;
parallaxView4.jump = true;
parallaxView4.shoot = false;
parallaxView4.lastTurn3 = System.currentTimeMillis();
break;
default:
break;
}
}
}
ParallaxView.java
public class ParallaxView extends SurfaceView implements Runnable, SurfaceHolder.Callback {
static int bombed = 5;
boolean waitForTimer = false;
boolean waitForTimer2 = false;
boolean waitForTimer3 = false;
boolean recent = false;
Rect fromRect1;
Rect toRect1;
Rect fromRect2;
Rect toRect2;
boolean increment = false;
int numberOfshots = 0; // change to 0
int[] missiles = new int[200];
int alienBombYDelta = 0;
int alienBombYDelta2 = 0;
int alienBombXDelta = 20;
int alienBombXDelta2 = 30;
int p = 7;
int p2 = 13;
boolean once, once2 = true;
final int buggyXDisplacement = 450;
int jumpHeight = 0;
int xbuggy2 = 0;
boolean toggleDeltaY = true;
long lastTurn2 = System.currentTimeMillis();
long lastTurn3 = System.currentTimeMillis();
boolean toggleGround = true;
boolean jump = false;
boolean shoot = false;
int index = 0;
int missileOffSetY = 0;
static int score = 0;
double buggyXDistance = 0;
double distanceDelta = 1.15;
double retardation = 0.5;
boolean checkpointComplete = false;
boolean runOnce = true;
boolean passed = false;
List<Background> backgrounds;
int spacerocki, resID, explodeID, explodeID2, alienResID2;
boolean toggle = true;
private volatile boolean running;
private Thread gameThread = null;
Bitmap explode, buggy, alien, alien2, explode2, spacerock, spacerock2, hole;
boolean alienexplode = false;
TextView tvId;
TextView checkpointtextview;
TextView checkpointtextview2;
TextView checkpointtextview3;
TextView checkpointtextview4;
TextView checkpointtextview5;
TextView checkpointtextview6;
// For drawing
private Paint paint;
private Canvas canvas;
private SurfaceHolder ourHolder;
AttackingAlien alien3;
AttackingAlien alien4, alien5;
// Holds a reference to the Activity
Context context;
// Control the fps
long fps = 60;
// Screen resolution
int screenWidth;
int screenHeight;
boolean bexplode = false;
boolean brake = false;
boolean scoring = false;
// use Handler instead
// this runs for 4 seconds and not just once after a while
class BuggyExploded extends TimerTask {
public void run() {
distanceDelta = 1.15;
retardation = 0.5;
jumpHeight = 0;
bexplode=true;
}
}
// use Handler instead
class SetRecent extends TimerTask {
public void run() {
recent = false;
}
}
// use Handler instead
class ResetCheckpoint extends TimerTask {
public void run() {
Log.d("## sectionComplete", "sectionComplete " + sectionComplete);
if (sectionComplete == 0) Background.checkpoint = 'A';
if (sectionComplete == 1) Background.checkpoint = 'F';
if (sectionComplete == 2) Background.checkpoint = 'K';
if (sectionComplete == 3) Background.checkpoint = 'P';
if (sectionComplete == 4) Background.checkpoint = 'U';
//if (sectionComplete==5) Background.checkpoint = 'U';
}
}
private void update() {
// Update all the background positions
for (Background bg : backgrounds) {
bg.update(fps);
}
}
#Override
public void run() {
while (running) {
long startFrameTime = System.currentTimeMillis();
update();
if (alienBombXDelta > screenWidth - 250 || alienBombXDelta < 10) { // UFO change direction
p = -p;
}
if (alienBombXDelta2 > screenWidth - 250 || alienBombXDelta2 < 10) { // UFO2 change direction
p2 = -p2;
}
draw();
// Calculate the fps this frame
long timeThisFrame = System.currentTimeMillis() - startFrameTime;
if (timeThisFrame >= 1) {
fps = 1000 / timeThisFrame;
}
}
}
private void checkJump() {
if (System.currentTimeMillis() - lastTurn3 >= 650) { // 650 means how long the vehicle is in the air at a jump
// Change direction here
jump = false;
lastTurn3 = System.currentTimeMillis();
}
}
private void checkBuggyBombed() {
if (recent) {
// use handlers instead
new Timer().schedule(new BuggyExploded(), 4000);
new Timer().schedule(new SetRecent(), 10000);
new Timer().schedule(new ResetCheckpoint(), 1000);
}
}
private void makeShots(Bitmap b) {
for (int i1 = 0; i1 < numberOfshots; i1++) {
if (shoot) {
canvas.drawText("o", (float) (missiles[i1] + buggyXDistance + 450), (float) (screenHeight * 0.7) - jumpHeight, paint); // add to y the jump height
canvas.drawText("o", (float) (buggyXDistance + 185 + 400), screenHeight / 110 * 95 - missiles[i1] - xbuggy2, paint);
}
if (i1 == numberOfshots - 1 && missiles[i1] > screenWidth) {
if (numberOfshots > 0) numberOfshots--;
if (index > 0) index--;
}
}
}
private void updateDeltas() {
alienBombXDelta = alienBombXDelta + p;
//make sure alien does not move too low
if (alienBombYDelta + 1 > screenHeight / 2)
alienBombYDelta=alienBombYDelta-2;
if (!toggleDeltaY)
alienBombYDelta++;
else
alienBombYDelta--;
alienBombXDelta2 = alienBombXDelta2 + p2;
if (!toggleDeltaY)
alienBombYDelta2++;
else
alienBombYDelta2--;
}
//use a Handler instead
private void changeDirections() {
if (System.currentTimeMillis() - lastTurn2 >= 7000) {
// Change direction here
toggleDeltaY = !toggleDeltaY;
lastTurn2 = System.currentTimeMillis();
}
}
//try to improve this
private void controlVelocity() {
if (!brake && buggyXDistance > 0) buggyXDistance = buggyXDistance + distanceDelta;
else if (brake && buggyXDistance > 0) buggyXDistance = buggyXDistance - retardation;
}
TextView tvId1;
int sectionComplete = 0;
private void drawDetails() {
//draw a background color
}
private void makeShots() {
for (int n = 0; n < numberOfshots; n++)
missiles[n] = missiles[n] + 20;
}
public void changeText() {
if (scoring) {
((Activity) this.getContext()).runOnUiThread(new Runnable() {
#Override
public void run() {
String str = "Player 1 " + String.format("%06d", score);
tvId.setText(str);
scoring = false;
}
});
}
}
double lastTurn4 = System.currentTimeMillis();
//change to handler
private void checkFire() {
if (System.currentTimeMillis() - lastTurn4 >= 118500) { // 18500 means how often the alien fires
lastTurn4 = System.currentTimeMillis();
missileOffSetY = 0;
}
}
private void draw() {
if (retardation > 0.5)
distanceDelta = 0;
if (distanceDelta > 0)
retardation = 0.5;
if (ourHolder.getSurface().isValid()) {
//First we lock the area of memory we will be drawing to
canvas = ourHolder.lockCanvas();
if (checkpointComplete) {
((Activity) this.getContext()).runOnUiThread(new Runnable() {
#Override
public void run() {
//
}
});
canvas.drawColor(Color.BLACK);
((ParallaxActivity) getContext()).stopWatch.stop();
paint.setTextSize(60);
String s2 = "TIME TO REACH POINT \"" + Background.checkpoint + "\"\n";
if (runOnce) {
for (int q = 0; q < s2.length(); q++) {
final String s2f = s2;
final int r = q;
((Activity) this.getContext()).runOnUiThread(new Runnable() {
#Override
public void run() {
checkpointtextview.setTextColor(Color.RED);
checkpointtextview.append(Character.toString(s2f.charAt(r)));
}
});
try {
Thread.sleep(50);
} catch (InterruptedException ie) {
}
}
}
String str = String.format("%03d", ((ParallaxActivity) this.getContext()).countUp);
String s3 = "YOUR TIME : " + str;
if (runOnce) {
for (int q = 0; q < s3.length(); q++) {
final String s3f = s3;
final int r = q;
((Activity) this.getContext()).runOnUiThread(new Runnable() {
#Override
public void run() {
checkpointtextview2.setTextColor(Color.parseColor("#ADD8E6"));
checkpointtextview2.append(Character.toString(s3f.charAt(r)));
}
});
try {
Thread.sleep(50);
} catch (InterruptedException ie) {
}
}
}
String s4 = "THE AVERAGE TIME : 060";
if (runOnce) {
for (int q = 0; q < s4.length(); q++) {
final String s4f = s4;
final int r = q;
((Activity) this.getContext()).runOnUiThread(new Runnable() {
#Override
public void run() {
checkpointtextview3.setTextColor(Color.parseColor("#ADD8E6"));
checkpointtextview3.append(Character.toString(s4f.charAt(r)));
}
});
try {
Thread.sleep(50);
} catch (InterruptedException ie) {
}
}
}
String s5 = "TOP RECORD : 060";
if (runOnce) {
for (int q = 0; q < s5.length(); q++) {
final String s5f = s5;
final int r = q;
((Activity) this.getContext()).runOnUiThread(new Runnable() {
#Override
public void run() {
checkpointtextview4.setTextColor(Color.RED);
checkpointtextview4.append(Character.toString(s5f.charAt(r)));
}
});
try {
Thread.sleep(50);
} catch (InterruptedException ie) {
}
}
}
String s6 = "GOOD BONUS POINTS : 1000";
if (runOnce) {
for (int q = 0; q < s6.length(); q++) {
final String s6f = s6;
final int r = q;
((Activity) this.getContext()).runOnUiThread(new Runnable() {
#Override
public void run() {
checkpointtextview5.setTextColor(Color.RED);
checkpointtextview5.append(Character.toString(s6f.charAt(r)));
}
});
try {
Thread.sleep(50);
} catch (InterruptedException ie) {
}
}
}
if (runOnce) {
score = score + 1000;
sectionComplete++;
recent = true;
}
runOnce = false;
// canvas.drawText("CHECKPOINT COMPLETE", (float) (screenWidth * 0.35), (float) (screenHeight * 0.45), paint);
((Activity) this.getContext()).runOnUiThread(new Runnable() {
#Override
public void run() {
Handler handler = new Handler();
handler.postDelayed(new Runnable() {
#Override
public void run() {
((ParallaxActivity) getContext()).startTime = SystemClock.elapsedRealtime();
((ParallaxActivity) getContext()).stopWatch.setBase(((ParallaxActivity) getContext()).startTime);
((ParallaxActivity) getContext()).stopWatch.start();
checkpointtextview.setText("");
checkpointtextview2.setText("");
checkpointtextview3.setText("");
checkpointtextview4.setText("");
checkpointtextview5.setText("");
checkpointtextview6.setText("");
String str = "Player 1 " + String.format("%06d", score);
tvId.setText(str);
scoring = false;
buggyXDistance = 0;
distanceDelta = 0;
retardation = 0;
checkpointComplete = false;
runOnce = true;
}
}, 3000);
}
});
} else {
if (bombed == 0) //GAME OVER
{
final int duration = Toast.LENGTH_SHORT;
((Activity) this.getContext()).runOnUiThread(new Runnable() {
#Override
public void run() {
final Toast toast = Toast.makeText(context, "GAME OVER!\nScore: " + score, duration);
toast.show();
Handler handler = new Handler();
handler.postDelayed(new Runnable() {
#Override
public void run() {
toast.cancel();
bombed = 5;
score = 0;
Background.checkpoint = 'A';
String str = "Player 1 " + String.format("%06d", score);
tvId.setText(str);
}
}, 3000);
}
});
}
// adjust vehicle physics when jumping
if (jump && jumpHeight < 300) {
jumpHeight = jumpHeight + 7;
if (distanceDelta < 3) distanceDelta = distanceDelta + 0.55;
} else if (jumpHeight > 0) {
jumpHeight = jumpHeight - 4;
if (distanceDelta < 3) distanceDelta = distanceDelta + 0.55;
}
if (shoot) {
xbuggy2 = xbuggy2 + 4;
}
checkFire();
checkJump();
// drawDetails();
canvas.drawColor(Color.argb(255, 0, 0, 0));
// Draw the background parallax
drawBackground(0);
// Draw the rest of the game
paint.setTextSize(60);
paint.setColor(Color.argb(255, 255, 255, 255));
checkBuggyBombed();
makeShots(alien);
changeDirections();
alien3.update(canvas, paint, toggleDeltaY, screenWidth, screenHeight);
recent=alien3.drawMissile(this, canvas, paint, buggyXDisplacement, buggyXDistance, buggy, jumpHeight, screenHeight);
if(recent) {
waitForTimer = true;
bexplode=true;
AttackingAlien.recent = true;
}
alien4.update(canvas, paint, toggleDeltaY, screenWidth, screenHeight);
boolean recent2=alien4.drawMissile(this, canvas, paint, buggyXDisplacement, buggyXDistance, buggy, jumpHeight, screenHeight);
if(recent || recent2) {
recent = true;
waitForTimer = true;
bexplode=true;
AttackingAlien.recent = true;
}
alien5.update(canvas, paint, toggleDeltaY, screenWidth, screenHeight);
boolean recent3=alien5.drawMissile(this, canvas, paint, buggyXDisplacement, buggyXDistance, buggy, jumpHeight, screenHeight);
if(recent || recent2 || recent3) {
recent = true;
waitForTimer = true;
bexplode=true;
AttackingAlien.recent = true;
Handler handler = new Handler(Looper.getMainLooper());
// this code runs after a while
handler.postDelayed(new Runnable() {
#Override
public void run() {
recent = false;
AttackingAlien.recent = false;
waitForTimer = false;
bexplode=false;
buggyXDistance = 0;
Log.d("postDelayed", "postDelayed ");
}
}, 5000);
}
checkBuggyBombed();
for (int i1 = 0; i1 < numberOfshots; i1++) {
alien3.checkBeingHit(missiles, buggyXDisplacement, buggyXDistance, canvas, explode2, paint, score, this, i1, xbuggy2);
alien4.checkBeingHit(missiles, buggyXDisplacement, buggyXDistance, canvas, explode2, paint, score, this, i1, xbuggy2);
alien5.checkBeingHit(missiles, buggyXDisplacement, buggyXDistance, canvas, explode2, paint, score, this, i1, xbuggy2);
}
drawBackground(1);
// canvas.drawText("X", (float) (50 + buggyXDistance)+buggy.getWidth()/2, (float) (screenHeight * 0.3) - jumpHeight+buggy.getHeight(), paint);
paint.setTextSize(60);
canvas.drawText("A E J O T Z", (float) (screenWidth * 0.7), (float) (screenHeight * 0.15), paint);
// Prevent buggy from moving outside horizontal screen
if (!brake && buggyXDisplacement + buggyXDistance > screenWidth - buggy.getWidth() - 200)
buggyXDistance = screenWidth - buggy.getWidth() - 200;
//Log.d("buggyXDistance", "buggyXDistance " + buggyXDistance);
if (!bexplode && !waitForTimer && !checkpointComplete)
canvas.drawBitmap(buggy, (float) (buggyXDisplacement + buggyXDistance), (float) (screenHeight * 0.5) - jumpHeight, paint);
else if (bexplode && !checkpointComplete) {
canvas.drawBitmap(explode, (float) (buggyXDisplacement + buggyXDistance), (float) (screenHeight * 0.5) - jumpHeight, paint);
distanceDelta = 0;
retardation = 0;
}
int inc = 0;
for (int i = 0; i < bombed; i++) {
canvas.drawBitmap(Bitmap.createScaledBitmap(buggy, (int) (0.50 * (buggy.getWidth() / 3)), (int) (0.50 * buggy.getHeight() / 3), false), inc, 100, paint);
inc = inc + buggy.getWidth() / 4;
}
makeShots();
updateDeltas();
controlVelocity();
}
ourHolder.unlockCanvasAndPost(canvas);
}
}
// Clean up our thread if the game is stopped
public void pause() {
running = false;
try {
gameThread.join();
} catch (InterruptedException e) {
// Error
//e.printStackTrace();
}
}
// Make a new thread and startMissile it
// Execution moves to our run method
public void resume() {
running = true;
gameThread = new Thread(this);
gameThread.start();
}
int craterX = -550;
}
The complete code is available on request because of SO limitation of 30000 chars.
I unfortunately cannot supply you with the code for this right now (since I am at work currently), but one thing you can do is to have your fall speed (acceleration) modified depending on whether the jump button is held.
ie.
// Put this into your constant gravity function
// Or whatever you have that makes the player fall
// This should be altering a local gravity modifier in the player
if(jumpKeyHeld) {
locaGravAccel = 0.5
} else {
locaGravAccel = 1.0
}
What this does is make your jump feel more responsive as you move upwards at a nice speed, and then fall straight down to the ground quickly. It also gives the famous tap-for-low-jump-hold-for-high-jump feeling that you find in Mario games and the like.
Unless I vastly misunderstood your question, I believe this will help with game feel for you. I've implemented similar features in other projects, and people have always responded positively to this change, often claiming it feels more like a genuine 'game'.
UPDATE: Since I've noticed you don't actually have a gravity function, here is one way to do it. It is in C# for unity2D, but this is how I learned to do it, so I know it is good.
Hi I've implemented a toggle button for sound on my SettingsMenu, it works within this screen however when I go back to my main menu the functionality is lost and it resets back to its initial state. Any ideas how I can make it so that once it is clicked, it keeps this selection throughout every screen. I will show you the toggle button class and the settings class in which it is implemented. Thanks.
package uk.ac.qub.eeecs.game.cardDemo;
public class ToggleButtonSound extends GameObject {
protected enum ButtonState {
ON, OFF
}
private ButtonState mButtonState = ButtonState.OFF;
private SoundManager sm;
protected Bitmap mOffBitmap;
protected Bitmap mOnBitmap;
protected Bitmap mOnHoverBitmap;
protected Bitmap mOffHoverBitmap;
private Game mGame;
public boolean mMusicMuted = false;
public boolean mSoundMuted = false;
private MusicManager mMusicManager;
public ToggleButtonSound(float x, float y, float width, float height,
String offBitmap,
String onBitmap,
// String OffHoverBitmap,
//String OnHoverBitmap,
GameScreen gameScreen,
Game game) {
super(x, y, width, height,
gameScreen.getGame().getAssetManager().getBitmap(offBitmap), gameScreen);
mGame = game;
AssetStore assetStore = gameScreen.getGame().getAssetManager();
mOffBitmap = assetStore.getBitmap(offBitmap);
// mOffHoverBitmap = assetStore.getBitmap(offBitmap);
mOnBitmap = assetStore.getBitmap(onBitmap);
//mOnHoverBitmap = assetStore.getBitmap(onBitmap);
sm = new SoundManager(mGame);
mMusicManager = new MusicManager(mGame);
}
private boolean mPushTriggered;
private boolean mIsPushed;
/**
* Update the button
*
* #param elapsedTime Elapsed time information
*/
public void update(ElapsedTime elapsedTime) {
// Consider any touch events occurring in this update
Input input = mGame.getInput();
BoundingBox bound = getBound();
// Check for a press release on this button
for (TouchEvent touchEvent : input.getTouchEvents()) {
if (touchEvent.type == TouchEvent.TOUCH_UP
&& bound.contains(touchEvent.x, touchEvent.y)) {
// A touch up has occured in this control
if (mButtonState == ButtonState.OFF) {
setToggled(true);
} else {
setToggled(false);
}
return;
}
}
for (int idx = 0; idx < TouchHandler.MAX_TOUCHPOINTS; idx++) {
if (input.existsTouch(idx)) {
if (bound.contains(input.getTouchX(idx), input.getTouchY(idx))) {
if (!mIsPushed) {
mPushTriggered = true;
if (mOnHoverBitmap != null && mOffHoverBitmap != null)
mBitmap = mButtonState == ButtonState.ON ? mOnHoverBitmap : mOffHoverBitmap;
mIsPushed = true;
}
return;
}
}
}
if (mIsPushed) {
mBitmap = mButtonState == ButtonState.ON ? mOnBitmap : mOffBitmap;
mIsPushed = false;
mPushTriggered = false;
}
}
public boolean isToggledOn() {
return mButtonState == ButtonState.ON;
}
public void setToggled(boolean on) {
if (on) {
mButtonState = ButtonState.ON;
mBitmap = mOnBitmap;
} else {
mButtonState = ButtonState.OFF;
mBitmap = mOffBitmap;
}
}
#Override
public void draw(ElapsedTime elapsedTime, IGraphics2D graphics2D,
LayerViewport layerViewport, ScreenViewport screenViewport) {
// Assumed to be in screen space so just draw the whole thing
drawScreenRect.set((int) (position.x - mBound.halfWidth),
(int) (position.y - mBound.halfWidth),
(int) (position.x + mBound.halfWidth),
(int) (position.y + mBound.halfHeight));
graphics2D.drawBitmap(mBitmap, null, drawScreenRect, null);
}
}
Settings menu
public class SettingsMenu extends GameScreen {
private ToggleButtonSound mToggleButtonSound;
public SettingsMenu(Game game) {
super("Settings Menu", game);
mSoundManager = new SoundManager(game);
mMenuScreen = new MenuScreen(game);
music = new MusicManager(game);
AssetStore assetManager = mGame.getAssetManager();
assetManager.loadAndAddBitmap("offswitch", "img/offswitch.png");
assetManager.loadAndAddBitmap("onswitch", "img/onswitch.png");
assetManager.loadAndAddBitmap("SoundOn", "img/Soundon.png");
assetManager.loadAndAddBitmap("SoundOff", "img/Soundoff.png");
assetManager.loadAndAddBitmap("SettingsBackground", "img/settingsbackground.png");
//Load in sound
assetManager.loadAndAddSound("mouseClick", "Sounds/clicksound.wav");
assetManager.loadAndAddMusic("BackgroundMusic", "Sounds/MenuScreenBackgroundMusic.mp3");
//initialise sound
int spacingX = mGame.getScreenWidth() / 3;
int spacingY = mGame.getScreenHeight() / 9;
mPlayButtonBound = new Rect(spacingX, 3 * spacingY, 2 * spacingX, 4 * spacingY);
mInventoryButtonBound = new Rect(spacingX, 5 * spacingY, 2 * spacingX, 6 * spacingY);
soundButtonBound = new Rect(game.getScreenWidth() / 100, game.getScreenWidth() / 100, game.getScreenWidth() / 10, game.getScreenWidth() / 10);
mSettingsBackground = new Rect(1, 1, game.getScreenWidth(), game.getScreenHeight());
mLayerViewport = new LayerViewport(0, 0, game.getScreenWidth(), game.getScreenHeight());
mScreenViewport = new ScreenViewport(0, 0, game.getScreenWidth(), game.getScreenHeight());
int toggleHeight = mGame.getScreenHeight()/3;
mToggleButtonMusic = new ToggleButtonMusic(game.getScreenWidth() / 100, game.getScreenWidth() / 200, game.getScreenWidth() / 10, game.getScreenWidth() / 20, "onswitch","offswitch","onswitch","offswitch", this, mGame);
mToggleButtonSound = new ToggleButtonSound(game.getScreenWidth() / 100, game.getScreenWidth() / 200, game.getScreenWidth() / 10, game.getScreenWidth() / 20, "onswitch","offswitch", this, mGame);
mToggleButtonMusic.setPosition(game.getScreenWidth()/2,game.getScreenHeight()/3);
mToggleButtonSound.setPosition(game.getScreenWidth()/2, toggleHeight*2);
//toggleB.getBitmap();
//toggleB.getBound();
}
private boolean mIsPushed;
#Override
public void update(ElapsedTime elapsedTime) {
music.isPlaying1();
//mToggleButtonSound.setToggled(soundMuted);
mToggleButtonMusic.update(elapsedTime);
mToggleButtonSound.update(elapsedTime);
Input input = mGame.getInput();
List<TouchEvent> touchEvents = input.getTouchEvents();
if (touchEvents.size() > 0) {
TouchEvent touchEvent = touchEvents.get(0);
if (touchEvent.type == TouchEvent.TOUCH_UP) {
if ((soundButtonBound.contains((int) touchEvent.x, (int) touchEvent.y))) {
mGame.getScreenManager().removeScreen("Settings Menu");
mMenuScreen = new MenuScreen(mGame);
mGame.getScreenManager().addScreen(mMenuScreen);
}
}
}
}
#Override
public void draw(ElapsedTime elapsedTime, IGraphics2D graphics2D) {
graphics2D.drawBitmap(mGame.getAssetManager().getBitmap("SettingsBackground"), null, mSettingsBackground, null);
mToggleButtonMusic.draw(elapsedTime,graphics2D,mLayerViewport, mScreenViewport);
mToggleButtonSound.draw(elapsedTime,graphics2D,mLayerViewport, mScreenViewport);
graphics2D.drawBitmap(mGame.getAssetManager().getBitmap("SoundOn"), null, soundButtonBound, null);
}
#Override
public boolean onBackPressed() {
return true;
}
}
I'm trying to implement an autorotation method for a SpriteAnimation class (trying to port SpriteAnimation).
Everything is working, the sprite draws correctly (it uses the correct animations) it just doesn't rotate properly if at all, normally it seems to point to the origin (0, 0) only after its target matches its position while I want its rotation to update as it moves, i've tried both degrees and radians, neither of which work. It should be rotating to match the current direction it's going. I've been struggling with this for about a week now, but still have not gotten the desired result.
Full Code Here
Relevant code:
From SpriteAnimation
// The x position of the sprite's upper left corner pixel.
public int getX() { return (int)position.x; }
public void setX(int value)
{
prevPosition.x = position.x;
position.x = value;
updateRotation();
}
// The y position of the sprite's upper left corner pixel.
public int getY() { return (int)position.y; }
public void setY(int value)
{
prevPosition.y = position.y;
position.y = value;
updateRotation();
}
void updateRotation()
{
if (rotateByPosition)
{
Vector2 rotationVector = new Vector2(position.x - prevPosition.x, position.y - prevPosition.y);
rotationRad = rotationVector.angle();
rotationDeg = rotationRad * MathUtils.radiansToDegrees;
}
}
public void MoveBy(int x, int y)
{
prevPosition = new Vector2(position);
position.x += x;
position.y += y;
updateRotation();
}
public void Update(float deltaTime)
{
// Don't do anything if the sprite is not animating
if (animating)
{
// If there is not a currently active animation
if (getCurrentFrameAnimation() == null)
{
// Make sure we have an animation associated with this sprite
if (animations.size() > 0)
{
// Set the active animation to the first animation
// associated with this sprite
String[] sKeys = new String[animations.size()];
int index = 0;
for (Entry<String, FrameAnimation> mapEntry : animations.entrySet()) {
sKeys[index] = mapEntry.getKey();
index++;
}
setCurrentAnimation(sKeys[0]);
}
else
{
return;
}
}
// Run the Animation's update method
getCurrentFrameAnimation().Update(deltaTime);
// Check to see if there is a "follow-up" animation named for this animation
if (getCurrentFrameAnimation().getNextAnimation() != null && !getCurrentFrameAnimation().getNextAnimation().isEmpty())
{
// If there is, see if the currently playing animation has
// completed a full animation loop
if (getCurrentFrameAnimation().getPlayCount() > 0)
{
// If it has, set up the next animation
setCurrentAnimation(getCurrentFrameAnimation().getNextAnimation());
}
}
}
}
public void Draw(SpriteBatch spriteBatch, int xOffset, int yOffset)
{
updateRotation(); // Calling this while testing to make sure that it is being called
spriteBatch.draw(getCurrentTextureRegion(), getPosition().x + xOffset - center.x, getPosition().y + yOffset - center.y, center.x, center.y, getCurrentFrameAnimation().getFrameWidth(), getCurrentFrameAnimation().getFrameHeight(), 1.0f, 1.0f, rotationRad);
}
TestScreen class
package com.darkstudio.darkisle.screens;
import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.Screen;
import com.badlogic.gdx.graphics.GL20;
import com.badlogic.gdx.graphics.OrthographicCamera;
import com.badlogic.gdx.graphics.Texture;
import com.badlogic.gdx.math.Vector2;
import com.badlogic.gdx.math.Vector3;
import com.darkstudio.darkisle.DarkIsle;
import com.delib.engine.sprite.MobileSprite;
/**
* Created by DarkEnder on 2017/06/27.
*/
public class TestScreen implements Screen {
final DarkIsle game;
OrthographicCamera camera;
Texture tankTexture;
MobileSprite mouseTank;
public TestScreen(final DarkIsle game)
{
this.game = game;
camera = new OrthographicCamera();
configureCamera();
tankTexture = new Texture(Gdx.files.internal("MulticolorTanks.png"));
mouseTank = new MobileSprite(tankTexture, 32, 32);
mouseTank.getSprite().AddAnimation("red", 0, 32, 32, 32, 8, 0.1f);
mouseTank.getSprite().AddAnimation("purple", 0, 128, 32, 32, 8, 0.1f, "red");
mouseTank.getSprite().AddAnimation("yellow", 0, 64, 32, 32, 8, 0.1f);
mouseTank.getSprite().setAutoRotate(true);
mouseTank.setPosition(new Vector2(100, 100));
mouseTank.setTarget(new Vector2(mouseTank.getPosition()));
mouseTank.setIsPathing(true);
mouseTank.setEndPathAnimation("yellow");
mouseTank.setLoopPath(false);
mouseTank.setSpeed(2);
}
#Override
public void show() {
}
#Override
public void render(float delta) {
Gdx.gl.glClearColor(0, 0, 0, 1);
Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT | GL20.GL_DEPTH_BUFFER_BIT);
update();
game.batch.begin();
mouseTank.draw(game.batch);
game.batch.end();
}
#Override
public void resize(int width, int height) {
}
#Override
public void pause() {
}
#Override
public void resume() {
}
#Override
public void hide() {
}
#Override
public void dispose() {
}
private void configureCamera()
{
Vector3 camPos = new Vector3(camera.position);
float size = 800;
float cameraWidth = 0;
float cameraHeight = 0;
if (Gdx.graphics.getHeight() < Gdx.graphics.getWidth())
{
cameraWidth = size;
cameraHeight = size * Gdx.graphics.getHeight() / Gdx.graphics.getWidth();
}
else
{
cameraWidth = size * Gdx.graphics.getWidth() / Gdx.graphics.getHeight();
cameraHeight = size;
}
camera.setToOrtho(true, cameraWidth, cameraHeight);
camera.position.set(camPos.x + camera.viewportWidth / 2f, camPos.y + camera.viewportHeight / 2f, 0);
}
private void update() {
int xTouch = Gdx.input.getX(0);
int yTouch = Gdx.input.getY(0);
mouseTank.setTarget(xTouch, yTouch);
mouseTank.update(Gdx.graphics.getDeltaTime());
}
}
Edit: Just saying now that I've already tried using atan2 in MathUtils and it gave the same results.
The MobileSprite update method
public void update(float deltaTime)
{
if (active && movingTowardsTarget)
{
if ((target != null))
{
// Get a vector pointing from the current location of the sprite
// to the destination.
Vector2 Delta = new Vector2(target.x - sprite.getX(), target.y - sprite.getY());
if (Delta.len() > getSpeed())
{
Delta.nor();
Delta.scl(getSpeed());
getPosition().add(Delta);
}
else
{
if (target == sprite.getPosition())
{
if (pathing)
{
if (queuePath.size() > 0)
{
target = queuePath.remove();
if (loopPath)
{
queuePath.remove(target);
}
}
else
{
if (!(endPathAnimation == null))
{
if (!(getSprite().getCurrentAnimation() == endPathAnimation))
{
getSprite().setCurrentAnimation(endPathAnimation);
}
}
if (deactivateAtEndOfPath)
{
setIsActive(false);
}
if (hideAtEndOfPath)
{
setIsVisible(false);
}
}
}
}
else
{
sprite.setPosition(target);
}
}
}
}
if (active)
sprite.Update(deltaTime);
}
To get the radians angle:
void updateRotation()
{
if (rotateByPosition)
{
Vector2 rotationVector = new Vector2(position.x - prevPosition.x, position.y - prevPosition.y);
rotationRad = rotationVector.angleRad();
rotationDeg = rotationRad * MathUtils.radiansToDegrees;
}
}
and the draw method needs a degrees angle:
spriteBatch.draw(
getCurrentTextureRegion(),
getPosition().x + xOffset - center.x,
getPosition().y + yOffset - center.y,
center.x, center.y,
getCurrentFrameAnimation().getFrameWidth(), getCurrentFrameAnimation().getFrameHeight(),
1.0f, 1.0f,
rotationDeg
);
The screen mouse position is from the upper left corner the renderer needs from the bottom left corner:
int xTouch = Gdx.input.getX(0);
int yTouch = Gdx.graphics.getHeight() - Gdx.input.getY(0);
My app has 2 activities a main menu and a game play activity. you click play to start the game play and when you die you can click back to go to the main menu. For some reason when i click play for the third time (meaning ive died and went back to the main menu twice) the game crashes with this error.
FATAL EXCEPTION: main
Process: com.example.jordanschanzenbach.myapplication, PID: 1875
java.lang.OutOfMemoryError: Failed to allocate a 360012 byte allocation with 79976 free bytes and 78KB until OOM
at dalvik.system.VMRuntime.newNonMovableArray(Native Method)
at android.graphics.BitmapFactory.nativeDecodeAsset(Native Method)
at android.graphics.BitmapFactory.decodeStream(BitmapFactory.java:700)
at android.graphics.BitmapFactory.decodeResourceStream(BitmapFactory.java:535)
at android.graphics.BitmapFactory.decodeResource(BitmapFactory.java:558)
at android.graphics.BitmapFactory.decodeResource(BitmapFactory.java:588)
at com.example.jordanschanzenbach.myapplication.Obstacle.<init>(Obstacle.java:44)
at com.example.jordanschanzenbach.myapplication.ObstacleManager.populateObstacles(ObstacleManager.java:57)
at com.example.jordanschanzenbach.myapplication.ObstacleManager.<init>(ObstacleManager.java:38)
at com.example.jordanschanzenbach.myapplication.GamePlayScene.<init>(GamePlayScene.java:41)
at com.example.jordanschanzenbach.myapplication.GamePanel.<init>(GamePanel.java:29)
at com.example.jordanschanzenbach.myapplication.MainActivity.onCreate(MainActivity.java:27)
that's not the entire error message but i think i gave you what you needed. Now i tried to make an animation for the obstacles to be spikes instead of just black rectangles but for some reason the spikes will not display and i think it might correspond with the error i receive. But i don't understand why the spikes wont display as i used the same code for the animation for my player.
here is the code for my obstacle
public class Obstacle implements GameObject
{
private Rect rectangle;
private Rect rectangle2;
private int color;
private Animation falling;
private Animation fallingStill;
private Animation fallingAgain;
private AnimationManagerObstacle animationManagerObstacle;
public Rect getRectangle()
{
return rectangle;
}
public void IncrementY(float y )
{
rectangle.top += y;
rectangle.bottom += y;
rectangle2.top += y;
rectangle2.bottom += y;
}
public Obstacle(int rectHeight, int color, int startX, int startY, int playerGap)
{
this.color = color;
BitmapFactory bitfac = new BitmapFactory();
Bitmap fallings = bitfac.decodeResource(GlobalVariables.CURRENT_CONTEXT.getResources(), R.drawable.spikesupsidedown);
Bitmap fallingStills = bitfac.decodeResource(GlobalVariables.CURRENT_CONTEXT.getResources(), R.drawable.spikesupsidedown);
Bitmap fallingAgains = bitfac.decodeResource(GlobalVariables.CURRENT_CONTEXT.getResources(), R.drawable.spikesupsidedown);
falling = new Animation(new Bitmap[]{fallings}, 2);
fallingStill = new Animation(new Bitmap[]{fallings, fallingStills}, 0.5f);
fallingAgain = new Animation(new Bitmap[]{fallings, fallingAgains}, 0.5f);
animationManagerObstacle = new AnimationManagerObstacle(new Animation[]{falling, fallingStill, fallingAgain});
rectangle = new Rect(0, startY, startX, startY + rectHeight);
rectangle2 = new Rect(startX + playerGap, startY, GlobalVariables.SCREEN_WIDTH, startY + rectHeight);
}
public boolean playerCollide(RectPlayer player)
{
return Rect.intersects(rectangle, player.getRectangle()) || Rect.intersects(rectangle2, player.getRectangle());
}
#Override
public void draw(Canvas canvas)
{
Paint paint = new Paint();
paint.setColor(color);
animationManagerObstacle.draw(canvas, rectangle);
canvas.drawRect(rectangle, paint);
canvas.drawRect(rectangle2, paint);
}
#Override
public void update()
{
animationManagerObstacle.update();
}
public void update(Point point)
{
float oldTop = rectangle.top;
rectangle.set(point.x - rectangle.width() / 2,
point.y - rectangle.height() / 2,
point.x + rectangle.width() / 2,
point.y + rectangle.height() / 2);
int state = 0;
if (rectangle.left - oldTop > 1)
{
state = 1;
}
else if (rectangle.left - oldTop < 2)
{
state = 2;
}
animationManagerObstacle.playAnim(state);
animationManagerObstacle.update();
}
}
this is the line that the error message points to
Bitmap fallings = bitfac.decodeResource(GlobalVariables.CURRENT_CONTEXT.getResources(), R.drawable.spikesupsidedown);
here is my obstacle manager where i add and display the obstacles
public class ObstacleManager
{
private ArrayList<Obstacle> obstacles;
private int playerGap;
private int obstacleGap;
private int obstacleHeight;
private int color;
private long startTime;
private long initTime;
private int score = 0;
public ObstacleManager(int playerGap, int obstacleGap, int obstacleHeight, int color)
{
this.playerGap = playerGap;
this.obstacleGap = obstacleGap;
this.obstacleHeight = obstacleHeight;
this.color = color;
startTime = initTime = System.currentTimeMillis();
obstacles = new ArrayList<>();
populateObstacles();
}
public boolean playerCollide(RectPlayer player)
{
for(Obstacle ob : obstacles)
{
if(ob.playerCollide(player))
return true;
}
return false;
}
private void populateObstacles()
{
int currY = -5 * GlobalVariables.SCREEN_HEIGHT / 4;
while(currY < 0)
{
int xStart = (int)(Math.random()*(GlobalVariables.SCREEN_WIDTH - playerGap));
obstacles.add(new Obstacle(obstacleHeight, color, xStart, currY, playerGap));
currY += obstacleHeight + obstacleGap;
}
}
public void update()
{
if (GlobalVariables.GAMEOVER)
{
for (int i = 0; i < 3; i++)
{
obstacles.remove(obstacles.size() - 2);
}
}
int elapsedTime = (int)(System.currentTimeMillis() - startTime);
startTime = System.currentTimeMillis();
float speed = (float)(Math.sqrt((1 + startTime - initTime) / 1750.0)) * GlobalVariables.SCREEN_HEIGHT /17500.0f;
for(Obstacle ob : obstacles)
{
ob.IncrementY(speed * elapsedTime);
}
if(obstacles.get(obstacles.size() - 1).getRectangle().top >= GlobalVariables.SCREEN_HEIGHT * 3/4)
{
int xStart = (int)(Math.random()*(GlobalVariables.SCREEN_WIDTH - playerGap));
obstacles.add(0, new Obstacle(obstacleHeight, color, xStart,
obstacles.get(0).getRectangle().top - obstacleHeight - obstacleGap, playerGap));
obstacles.remove(obstacles.size() - 1);
score++;
if (score > GlobalVariables.HIGHSCORE)
GlobalVariables.HIGHSCORE = score;
}
}
public void draw(Canvas canvas)
{
for(Obstacle ob : obstacles)
ob.draw(canvas);
Paint paint = new Paint();
paint.setTextSize(100);
paint.setColor(Color.RED);
canvas.drawText("" + score, 50, 50 + paint.descent() - paint.ascent(), paint);
canvas.drawText("HighScore: " + GlobalVariables.HIGHSCORE, GlobalVariables.SCREEN_WIDTH / 2 + 50, 50 + paint.descent() - paint.ascent(), paint);
}
}
here is my animation manager
public class AnimationManager
{
private Animation[] animations;
private int animationsIndex = 0;
public AnimationManager(Animation[] animations)
{
this.animations = animations;
}
public void playAnim(int index)
{
for (int i = 0; i < animations.length; i++)
{
if (i == index)
{
if (!animations[index].isPlaying())
{
animations[i].play();
}
}
else
{
animations[i].stop();
}
}
animationsIndex = index;
}
public void draw(Canvas canvas, Rect rect)
{
if (animations[animationsIndex].isPlaying())
{
animations[animationsIndex].draw(canvas, rect);
}
}
public void update()
{
if (animations[animationsIndex].isPlaying())
{
animations[animationsIndex].update();
}
}
}
and finally my animation class
public class Animation
{
private Bitmap[] frames;
private int frameIndex;
private boolean isPlaying = false;
public boolean isPlaying()
{
return isPlaying;
}
public void play()
{
isPlaying = true;
frameIndex = 0;
lastFrame = System.currentTimeMillis();
}
public void stop()
{
isPlaying = false;
}
private float frameTime;
private long lastFrame;
public Animation(Bitmap[] frames, float animTime)
{
this.frames = frames;
frameIndex = 0;
frameTime = animTime / frames.length;
lastFrame = System.currentTimeMillis();
}
public void draw(Canvas canvas, Rect destination)
{
if (!isPlaying)
return;
scaleRect(destination);
canvas.drawBitmap(frames[frameIndex], null, destination, new Paint());
}
private void scaleRect(Rect rect)
{
float whRatio = (float)(frames[frameIndex].getWidth() / frames[frameIndex].getHeight());
if (rect.width() > rect.height())
{
rect.left = rect.right - (int)(rect.height() * whRatio);
}
else
{
rect.top = rect.bottom - (int)(rect.width() * (1 / whRatio));
}
}
public void update()
{
if (!isPlaying)
return;
if (System.currentTimeMillis() - lastFrame > frameTime * 1000)
{
frameIndex++;
if (frameIndex >= frames.length)
frameIndex = 0;
lastFrame = System.currentTimeMillis();
}
}
}
i dont think you necessarily needed all that but just in case :). thanks for any comments to improve or help.
OutOfMemoryError basically means that you tried to use more memory than you can. In this specific example you (bitmap) tried to allocate 360KB though you could only allocate 78KB more. You might have a memory leak somewhere or your bitmap might be too big. I think your Bitmaps might be leaking memory. I'm not an Android expert though.
I'd recommend you to create a method in Obstacle like recycle or something along this way. Then every time you remove Obstacle from obstacles in ObstacleManager call that method. In that method you should aim to recycle all no longer used bitmaps. I'd do it by calling either Animation#recycle on every of your animations or calling AnimationManagerObstacle#recycle. (Animation#recycle would be meant to call recycle on every one of your frames.)
I'm trying to make a small app that bounces balls around the frame of the window. When I add more than 1 ball to the list; it doesn't loop through them as expected.
Here's my code:
The Main Class:
public class Main extends JFrame {
private static final long serialVersionUID = 1L;
private List<Ball> balls = new ArrayList<Ball>();
private List<Ball> tempballs = new ArrayList<Ball>();
private static Timer t;
public void addBall(Ball b) {
tempballs.add(b);
}
public void initUI() {
this.setSize(500, 500);
this.setDefaultCloseOperation(3);
this.setVisible(true);
}
private BufferStrategy bs;
private Random r = new Random();
public void paint() {
if (!tempballs.isEmpty()) {
balls.addAll(tempballs);
tempballs = new ArrayList<Ball>();
}
int i = 0;
System.out.println(balls.size());
for (Ball b : new ArrayList<Ball>(balls)) {
i++;
System.out.println(i);
if ((bs = this.getBufferStrategy()) == null) {
this.createBufferStrategy(2);
return;
}
if (bs.contentsLost() || bs.contentsRestored()) {
return;
}
if (b.y >= this.getHeight() - 100) {
b.ydirection = -r.nextDouble() * 5;
}
if (b.y < 20) {
b.ydirection = r.nextDouble() * 5;
}
if (b.x >= this.getWidth() - 100) {
b.xdirection = -r.nextDouble() * 5;
}
if (b.x < 0) {
b.xdirection = r.nextDouble() * 5;
}
b.x += b.xdirection;
b.y += b.ydirection;
if (b.xdirection > 0)
b.xdirection += 0.1;
else
b.xdirection += -0.1;
if (b.ydirection > 0)
b.ydirection += 0.1;
else
b.ydirection += -0.1;
Graphics g = bs.getDrawGraphics();
g.fillOval((int) b.x, (int) b.y, 100, 100);
bs.show();
g.dispose();
bs.dispose();
}
i = 0;
}
public static void main(String[] args) {
try {
final Main m = new Main();
m.addMouseListener(new Mouse(m));
m.initUI();
t = new Timer();
TimerTask tt = new TimerTask() {
#Override
public void run() {
m.paint();
}
};
t.schedule(tt, Calendar.getInstance().getTime(), 20);
} catch (ConcurrentModificationException e) {
e.printStackTrace();
}
}
}
Here's the Ball class:
public class Ball {
private Random r = new Random();
public double y, x, ydirection, xdirection;
public Ball(int x, int y) {
this.y = y;
this.x = x;
ydirection = r.nextGaussian() * 5;
xdirection = r.nextGaussian() * 5;
}
}
and the mouse listener:
public class Mouse implements MouseListener {
Main m;
public Mouse(Main m) {
this.m = m;
}
#Override
public void mouseClicked(MouseEvent e) {
m.addBall(new Ball(e.getX(), e.getY()));
System.out.println("cl");
}
#Override
public void mouseEntered(MouseEvent arg0) {
}
#Override
public void mouseExited(MouseEvent arg0) {
}
#Override
public void mousePressed(MouseEvent arg0) {
}
#Override
public void mouseReleased(MouseEvent arg0) {
}
}
Additional details:
It only loops through the first item in the list, but the list size grows.
I'm using java 6, but I will change versions if needed.
If your loop does not behave as intended, try to find out why it is cancelled. You have two return statements in your loop which could cause this behavior. Make sysouts before those returns to figure out which one is the cause. Then find out why your if around the return is true. To digg deeper you can use your IDE's debugging mode and place breakpoints at interesting lines or use the step mode to run one line of code at a time.
Besides this you can place both if before the loop, the values they are checking should not change while you are in the paint() function (you are using the UI thread which might change them).
You have below return statements inside the loop. If the execution reaches any of the return statements, the loop ends.
Figure out, if you are entering these conditions and returning for the first value in the list.
if ((bs = this.getBufferStrategy()) == null) {
this.createBufferStrategy(2);
return;
}
if (bs.contentsLost() || bs.contentsRestored()) {
return;
}