How to implement Accelerometer into app correctly - android

I want to control my simple android game (arcanoid) with accelerometer. Paddle in game is now being controlled with touch screen event. I want to control it with accelerometer.
I tried to implement accelerometer into GameActivity class which is "controlling" BreakOutView class like this:
public class GameActivity extends Activity implements SensorEventListener {
// gameView will be the view of the Menu_Layout
// It will also hold the logic of the Menu_Layout
// and respond to screen touches as well
BreakOutView breakoutView;
private SensorManager sManager;
Sensor accelerometer;
Paddle paddle;
float x;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
breakoutView = new BreakOutView(this);
sManager = (SensorManager) getSystemService(Context.SENSOR_SERVICE); // zisk managera
if (sManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER) != null) {
accelerometer = sManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);
sManager.registerListener(this, accelerometer, SensorManager.SENSOR_DELAY_NORMAL);
}
// Initialize gameView and set it as the view
setContentView(breakoutView);
}
// This method executes when the player starts the Game
#Override
protected void onResume() {
super.onResume();
sManager.registerListener(this,accelerometer,SensorManager.SENSOR_DELAY_NORMAL);
// Tell the gameView resume method to execute
breakoutView.resume();
}
// This method executes when the player quits the Menu_Layout
#Override
protected void onPause() {
super.onPause();
sManager.unregisterListener(this);
// Tell the gameView pause method to execute
breakoutView.pause();
}
#Override
public void onSensorChanged(SensorEvent sensorEvent) {
if (sensorEvent.sensor.getType() == Sensor.TYPE_ACCELEROMETER)
{
x = sensorEvent.values[0];
if (x > 0) {
breakoutView.paddle.setMovementState(paddle.LEFT);
}
else { breakoutView.paddle.setMovementState(paddle.RIGHT);
}
}
}
#Override
public void onAccuracyChanged(Sensor sensor, int i) {
}
}
This is code for BreakOutView class.
onTouchEvent method is now disabled because I want to use accelerometer to control Paddle in application.
public class BreakOutView extends SurfaceView implements Runnable{
// This is our thread
Thread gameThread = null;
// This is new. We need a SurfaceHolder
// When we use Paint and Canvas in a thread
// We will see it in action in the draw method soon.
SurfaceHolder ourHolder;
// A boolean which we will set and unset
// when the Menu_Layout is running- or not.
volatile boolean playing;
// Game is paused at the start
boolean paused = true;
// A Canvas and a Paint object
Canvas canvas;
Paint paint;
// This variable tracks the Menu_Layout frame rate
long fps;
Bitmap bitmapBob;
Bitmap bitmapBall;
Bitmap bitmapPaddal;
Bitmap bitmapBrick1;
Bitmap bitmapBrick2;
Bitmap bitmapBrick3;
// The size of the screen in pixels
int screenX;
int screenY;
// The players paddle
Paddle paddle;
// A ball
Ball ball;
// Up to 200 bricks
Brick[] bricks = new Brick[24];
int numBricks = 0;
// For sound FX
SoundPool soundPool;
int beep1ID = -1;
int beep2ID = -1;
int beep3ID = -1;
int loseLifeID = -1;
int explodeID = -1;
// The score
int score = 0;
int level = 1;
// Lives
int lives = 3;
Rect dest;
DisplayMetrics dm;
int densityDpi;
// When we initialize (call new()) on BreakOutView
// This special constructor method runs
public BreakOutView(Context context) {
super(context);
// The next line of code asks the
// SurfaceView class to set up our object.
// How kind.
// Initialize ourHolder and paint objects
ourHolder = getHolder();
paint = new Paint();
// Get a Display object to access screen details
WindowManager wm = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
Display display = wm.getDefaultDisplay();
// Load the resolution into a Point object
Point size = new Point();
// TODO target API < 13
display.getSize(size);
screenX = size.x;
screenY = size.y;
// using dpi to set sizes for objects
dm = context.getResources().getDisplayMetrics();
densityDpi = dm.densityDpi;
paddle = new Paddle(screenX, screenY, densityDpi);
// Create a ball
ball = new Ball(screenX, screenY);
// Load the sounds
// This SoundPool is deprecated but don't worry
soundPool = new SoundPool(10, AudioManager.STREAM_MUSIC, 0);
try {
// Create objects of the 2 required classes
AssetManager assetManager = context.getAssets();
AssetFileDescriptor descriptor;
// Load our fx in memory ready for use
descriptor = assetManager.openFd("beep1.wav");
beep1ID = soundPool.load(descriptor, 0);
descriptor = assetManager.openFd("beep2.wav");
beep2ID = soundPool.load(descriptor, 0);
descriptor = assetManager.openFd("beep3.wav");
beep3ID = soundPool.load(descriptor, 0);
descriptor = assetManager.openFd("loseLife.wav");
loseLifeID = soundPool.load(descriptor, 0);
descriptor = assetManager.openFd("explode.wav");
explodeID = soundPool.load(descriptor, 0);
} catch (IOException e) {
// Print an error message to the console
Log.e("error", "failed to load sound files");
}
// Load Images from resource files
bitmapBob = BitmapFactory.decodeResource(this.getResources(), R.drawable.wall);
bitmapBall = BitmapFactory.decodeResource(this.getResources(), R.drawable.ball);
bitmapPaddal = BitmapFactory.decodeResource(this.getResources(), R.drawable.ball);
bitmapBrick1 = BitmapFactory.decodeResource(this.getResources(), R.drawable.brick_red);
bitmapBrick2 = BitmapFactory.decodeResource(this.getResources(), R.drawable.brick_green);
bitmapBrick3 = BitmapFactory.decodeResource(this.getResources(), R.drawable.brick_monster);
//Make Sizes Depending on DPI
int heightX = densityDpi / 8;
float length_Paddal = densityDpi / 1.50f;
int height_Paddal = densityDpi / 7;
int brickWidth = screenX / 8;
int brickHeight = screenY / 10;
bitmapBall = getResizedBitmap(bitmapBall, heightX, heightX);
bitmapPaddal = getResizedBitmap(bitmapPaddal, length_Paddal, height_Paddal);
bitmapBrick1 = getResizedBitmap(bitmapBrick1, brickWidth, brickHeight);
bitmapBrick2 = getResizedBitmap(bitmapBrick2, brickWidth, brickHeight);
bitmapBrick3 = getResizedBitmap(bitmapBrick3, brickWidth, brickHeight);
// Create bricks for level 1
createBricksAndRestart(1);
}
public void createBricksAndRestart(int Xlevel) {
// Put the ball back to the start
ball.reset(screenX, screenY);
level = Xlevel;
switch (Xlevel) {
case 2:
// level 2
ball.xVelocity = 600;
ball.yVelocity = -1000;
break;
// level 3
case 3:
ball.xVelocity = 1000;
ball.yVelocity = -1400;
break;
// level 1
default:
ball.xVelocity = 400;
ball.yVelocity = -800;
break;
}
// Brick Size
int brickWidth = screenX / 8;
int brickHeight = screenY / 10;
// Build a wall of bricks
numBricks = 0;
for (int column = 0; column < 8; column++) {
for (int row = 0; row < 3; row++) {
bricks[numBricks] = new Brick(row, column, brickWidth, brickHeight);
numBricks++;
}
}
// if Game is over reset scores ,lives &Level
if (lives == 0) {
score = 0;
lives = 3;
level = 1;
}
}
#Override
public void run() {
while (playing) {
// Capture the current time in milliseconds in startFrameTime
long startFrameTime = System.currentTimeMillis();
// Update the frame
if (!paused) {
update();
}
// Draw the frame
draw();
// Calculate the fps this frame
// We can then use the result to
// time animations and more.
long timeThisFrame = System.currentTimeMillis() - startFrameTime;
if (timeThisFrame >= 1) {
fps = 1000 / timeThisFrame;
}
}
}
// Everything that needs to be updated goes in here
// Movement, collision detection etc.
public void update() {
// Move the paddle if required
paddle.update(fps);
ball.update(fps);
// Check for ball colliding with a brick
for (int i = 0; i < numBricks; i++) {
if (bricks[i].getVisibility()) {
if (RectF.intersects(bricks[i].getRect(), ball.getRect())) {
bricks[i].setInvisible();
ball.reverseYVelocity();
score = score + 10;
soundPool.play(explodeID, 1, 1, 0, 0, 1);
}
}
}
// Check for ball colliding with paddle
if (
ball.getRect().intersect(paddle.getRect()) ||
RectF.intersects(paddle.getRect(), ball.getRect()) ||
paddle.getRect().intersect(ball.getRect())
) {
ball.reverseYVelocity();
// ReverseX Direction + IncreaseX speed
if (paddle.getMovementState() == paddle.RIGHT && ball.xVelocity < 0 || paddle.getMovementState() == paddle.LEFT && ball.xVelocity > 0) {
ball.reverseXVelocity();
}
// SameX Direction + IncreaseX speed
else if (paddle.getMovementState() == paddle.RIGHT && ball.xVelocity > 0 || paddle.getMovementState() == paddle.LEFT && ball.xVelocity < 0) {
ball.sameXVelocity();
}
/*// Paddle is still, DecreaseX speed
else if (paddle.getMovementState() == paddle.STOPPED) {
ball.zeroXVelocity();
}*/
// Some intersection Bugs
ball.clearObstacleY(paddle.getRect().top - 20);
soundPool.play(beep1ID, 1, 1, 0, 0, 1);
}
// Bounce the ball back when it hits the bottom of screen
// And Lose a life
if (ball.getRect().bottom > screenY) {
ball.reverseYVelocity();
ball.clearObstacleY(screenY - 5);
// Lose a life
lives--;
soundPool.play(loseLifeID, 1, 1, 0, 0, 1);
if (lives == 0) {
paused = true;
//draw Loss;
canvas = ourHolder.lockCanvas();
paint.setColor(getResources().getColor(R.color.orange));
paint.setTextSize(getResources().getDimension(R.dimen.text_size_big));
canvas.drawText("أنت خسرت!",
screenX / 2 - (densityDpi / 1.90f), screenY / 2 + (densityDpi), paint);
ourHolder.unlockCanvasAndPost(canvas);
try {
// Wait 3 seconds then reset a new game
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
// Create bricks at level 1
createBricksAndRestart(1);
}
}
// Pause if cleared screen
if (score == numBricks * 10) {
// Create bricks at level 2
createBricksAndRestart(2);
// fix for a pause bug
// so that it won't Pause After finishing the Game
score = score + 10;
// Gift the player with 1 new live
lives = lives + 1;
} else if (score == (numBricks * 20) + 10) {
// Create bricks at level 3
createBricksAndRestart(3);
// fix for a pause bug
// so that it won't Pause After finishing the Game
score = score + 10;
// Gift the player with 2 new lives
lives = lives + 2;
}
// Pause if cleared screen
// if score equals to the whole Bricks scores after 3 levels
else if (score == (numBricks * 10 * 3) + 20) {
paused = true;
}
// Bounce the ball back when it hits the top of screen
if (ball.getRect().top < 0) {
ball.reverseYVelocity();
ball.clearObstacleY(40);
soundPool.play(beep2ID, 1, 1, 0, 0, 1);
}
// If the ball hits left wall bounce
if (ball.getRect().left < 0) {
ball.reverseXVelocity();
ball.clearObstacleX(2);
soundPool.play(beep3ID, 1, 1, 0, 0, 1);
}
// If the ball hits right wall Velocity
if (ball.getRect().right > screenX) {
ball.reverseXVelocity();
ball.clearObstacleX(screenX - 57);
soundPool.play(beep3ID, 1, 1, 0, 0, 1);
}
}
// Draw the newly updated scene
public void draw() {
// Make sure our drawing surface is valid or we crash
if (ourHolder.getSurface().isValid()) {
// Lock the canvas ready to draw
canvas = ourHolder.lockCanvas();
// Draw the background color
// canvas.drawColor(getResources().getColor(R.color.deeppurple));
dest = new Rect(0, 0, getWidth(), getHeight());
// Draw bob as background with dest size
canvas.drawBitmap(bitmapBob, null, dest, paint);
// Choose the brush color for drawing
paint.setColor(Color.argb(255, 255, 255, 255));
// Draw the ball
// canvas.drawCircle(ball.getRect().centerX(), ball.getRect().centerY(), 25, paint);
canvas.drawBitmap(bitmapBall, ball.getRect().left, ball.getRect().top, null);
// Draw the paddle
//canvas.drawRect(paddle.getRect(), paint);
canvas.drawBitmap(bitmapPaddal, paddle.getRect().left, paddle.getRect().top, null);
// Change the brush color for drawing
// paint.setColor(getResources().getColor(R.color.redorange));
// Draw the bricks if visible
for (int i = 0; i < numBricks; i++) {
if (bricks[i].getVisibility()) {
// canvas.drawRect(bricks[i].getRect(), paint);
switch (level) {
case 1:
canvas.drawBitmap(bitmapBrick1, bricks[i].getRect().left, bricks[i].getRect().top, null);
break;
case 2:
canvas.drawBitmap(bitmapBrick2, bricks[i].getRect().left, bricks[i].getRect().top, null);
break;
case 3:
canvas.drawBitmap(bitmapBrick3, bricks[i].getRect().left, bricks[i].getRect().top, null);
break;
}
}
}
// Choose the brush color for drawing
paint.setColor(Color.argb(255, 255, 255, 255));
// Draw the score
paint.setTextSize(getResources().getDimension(R.dimen.text_size));
// Score Text
canvas.drawText(
"النقاط: " + score
, screenX - (densityDpi / 1.50f), screenY / 2, paint);
// Lives Text
canvas.drawText("الصحة: " + lives
, densityDpi / 5, screenY / 2, paint);
// Levels Text
canvas.drawText("المرحلة: " + level
, screenX / 2 - (densityDpi / 5), screenY / 2 + (densityDpi / 5), paint);
// Has the player cleared the screen?
if (score >= (numBricks * 10 * 3) + 20) {
paint.setColor(getResources().getColor(R.color.colorAccent));
paint.setTextSize(getResources().getDimension(R.dimen.text_size_big));
canvas.drawText("أنت كسبت!", screenX / 2 - (densityDpi / 1.90f), screenY / 2 + (densityDpi / 1), paint);
}
// Draw everything to the screen
ourHolder.unlockCanvasAndPost(canvas);
}
}
// If GameActivity is paused/stopped
// shutdown our thread.
public void pause() {
playing = false;
try {
gameThread.join();
} catch (InterruptedException e) {
Log.e("Error:", "joining thread");
}
}
// If GameActivity is started
// start our thread.
public void resume() {
playing = true;
gameThread = new Thread(this);
gameThread.start();
}
// The SurfaceView class implements onTouchListener
// So we can override this method and detect screen touches.
/* #Override
public boolean onTouchEvent(MotionEvent motionEvent) {
switch (motionEvent.getAction() & MotionEvent.ACTION_MASK) {
// Player has touched the screen
case MotionEvent.ACTION_DOWN:
if (!(lives == 0)) {
paused = false;
}
// If touch motion > Half of the Screen
if (motionEvent.getX() > screenX / 2) {
// move paddle right
paddle.setMovementState(paddle.RIGHT);
} else {
// move paddle left
paddle.setMovementState(paddle.LEFT);
}
break;
// Player has removed finger from screen
case MotionEvent.ACTION_UP:
// paddle stopped
paddle.setMovementState(paddle.STOPPED);
break;
}
return true;
}*/
// Resize Bitmap function to Handle all the Images from resources the right size
public Bitmap getResizedBitmap(Bitmap bm, float newWidth, int newHeight) {
int width = bm.getWidth();
int height = bm.getHeight();
float scaleWidth = newWidth / width;
float scaleHeight = ((float) newHeight) / height;
// CREATE A MATRIX FOR THE MANIPULATION
Matrix matrix = new Matrix();
// RESIZE THE BIT MAP
matrix.postScale(scaleWidth, scaleHeight);
// "RECREATE" THE NEW BITMAP
Bitmap resizedBitmap = Bitmap.createBitmap(
bm, 0, 0, width, height, matrix, false);
bm.recycle();
return resizedBitmap;
}
}
And this is code for Paddle class:
import android.graphics.RectF;
public class Paddle {
// Which ways can the paddle move
public final int STOPPED = 0;
public final int LEFT = 1;
public final int RIGHT = 2;
int scrX;
// RectF is an object that holds four coordinates - just what we need
private RectF rect;
// How long and high our paddle will be
private float length;
private float height;
// X is the far left of the rectangle which forms our paddle
private float x;
// Y is the top coordinate
private float y;
// This will hold the pixels per second speedthat the paddle will move
private float paddleSpeed;
// Is the paddle moving and in which direction
private int paddleMoving = STOPPED;
private int MYscreenDPI;
// This the the constructor method
// When we create an object from this class we will pass
// in the screen width and height
public Paddle(int screenX, int screenY, int screenDPI) {
// Dynamic size based on each device DPI
length = screenDPI / 2;
height = screenDPI / 5;
MYscreenDPI = screenDPI;
scrX = screenX;
// Start paddle in roughly the sceen centre
x = screenX / 2;
y = screenY - screenDPI / 4.50f;
rect = new RectF(x, y, x + length, y + height);
// How fast is the paddle in pixels per second
paddleSpeed = 800;
}
public float getX() {
return x;
}
public void setX(float x) {
this.x = x;
}
public float getY() {
return y;
}
public void setY(float y) {
this.y = y;
}
// This is a getter method to make the rectangle that
// defines our paddle available in BreakoutView class
public RectF getRect() {
return rect;
}
public int getMovementState() {
return paddleMoving;
}
// This method will be used to change/set if the paddle is going left, right or nowhere
public void setMovementState(int state) {
paddleMoving = state;
}
// This update method will be called from update in BreakoutView
// It determines if the paddle needs to move and changes the coordinates
// contained in rect if necessary
public void update(long fps) {
if (paddleMoving == LEFT) {
// to fix Paddle going off the Screen
if (x >= -MYscreenDPI / 10)
// Decrement position
x = x - paddleSpeed / fps;
}
if (paddleMoving == RIGHT) {
// to fix Paddle going off the Screen
if (x <= scrX - length - MYscreenDPI / 14)
// Increment position
x = x + paddleSpeed / fps;
}
// Apply the New position
rect.left = x;
rect.right = x + length;
}
}
When I tried to run it application is always crashing and not working. When I try to use onTouchEvent method to control paddle it is not crashing but accelerometer is not working.
I know that I'm doing something bad. I'm trying to figure it out for like one week but I don't know how to make it working.
If you can tell me any suggestions I will be really thankful. Thank you for all responses.
--EDIT--
Here is error message from logcat when I clicked on new game in emulator:
2019-04-13 17:30:55.116 7082-7082/com.kazaky.breakout E/SensorManager: Exception dispatching input event. 2019-04-13 17:30:55.122 7082-7082/com.kazaky.breakout E/AndroidRuntime: FATAL EXCEPTION: main
Process: com.kazaky.breakout, PID: 7082
java.lang.NullPointerException: Attempt to invoke virtual method 'java.lang.Class java.lang.Object.getClass()' on a null object reference
at com.kazaky.breakout.GameActivity.onSensorChanged(GameActivity.java:73)
at android.hardware.SystemSensorManager$SensorEventQueue.dispatchSensorEvent(SystemSensorManager.java:833)
at android.os.MessageQueue.nativePollOnce(Native Method)
at android.os.MessageQueue.next(MessageQueue.java:326)
at android.os.Looper.loop(Looper.java:160)
at android.app.ActivityThread.main(ActivityThread.java:6669)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:493)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:858) 2019-04-13 17:31:06.794 7082-7117/com.kazaky.breakout E/Surface: queueBuffer: error queuing buffer to SurfaceTexture, -19 2019-04-13 17:31:06.794 7082-7117/com.kazaky.breakout E/Surface: queueBuffer (handle=0xe9f16140) failed (No such device)

As suggested by the Stack Trace you are trying to access a null object, which in this case I'm guessing sensroEvent in onSensorChanged.
Add a nullability check and return if sensorEvent is null, like so:
if (sensorEvent == null) return
and write the rest of the function after the Guard Condition.

Related

Am i going wrong?? using view ondraw and thread to make a animate game

i am new to android apps ,
i just start to write a simple game.
I used a Class extends View and using some thread to make a simple game
i wanna ask if i go wrong direction ? should i use other way to do it?
I think i am wrong on using thread in here right??
I just use
Game gameview;
gameview= new Game(this);
setContentView(gameview);
to call this in class which extends activity
public class Game extends View {
Bitmap background;
Bitmap nomouthbear;
Bitmap nomouthbearget;
Bitmap apple;
float bearX;
float bearY;
List<Apple> appleList;
int appleCount = 0;
float appleX;
float appleY;
float applescore;
float applespeed;
int totalscore = 0;
float canvasheight;
float canvaswidth;
float randomNumber;// random number for apple appear in X
float randomNumber2;// random number for score and speed
float randomNumber3;
int applecombo = 0; // count for how many apple that the bear eat in combo
boolean checkbeareat; // for check if bear eat any of apple or not.
boolean bearEating = false;
Thread thread2;
boolean ineatingthread = false;
int time = 20; // game time =60sec
public Game(Context context) {
super(context);
// setup image source
nomouthbear = BitmapFactory.decodeResource(getResources(),
R.drawable.nomouthbear_net);
apple = BitmapFactory.decodeResource(getResources(), R.drawable.apple);
nomouthbearget = BitmapFactory.decodeResource(getResources(),
R.drawable.nomouthbearget);
background = BitmapFactory.decodeResource(getResources(),
R.drawable.background);
// init bear x,y
bearX=0;
bearY =0;
// setup background
if (background != null) {
setBackgroundDrawable(new BitmapDrawable(background));
}
// open a list which hold all apple
appleList = new ArrayList<Apple>();
// make a thread to creat apple every 2 sec
Thread thread = new Thread() {
#Override
public void run() {
try {
while (true) {
sleep(2000);
randomNumber = (float) Math.random();
randomNumber2 = (float) Math.random();
// avoid start at edge point
appleX = canvaswidth * randomNumber;
if (appleX >= canvaswidth - apple.getWidth() / 2)
appleX = canvaswidth - apple.getWidth() / 2;
if (appleX <= nomouthbear.getWidth() / 2)
appleX = nomouthbear.getWidth();
applescore = 1000 * randomNumber2;
// check if speed too low
applespeed = 10 * randomNumber2;
if (applespeed <= 3) {
applespeed = 3;
}
// add new apple in the apple list
appleList.add(new Apple(appleX, 65, applescore,
applespeed));
appleCount++;
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
};
// start the thread
thread.start();
// thread for timer
Thread thread1 = new Thread() {
#Override
public void run() {
try {
while (true)
{
sleep(1000);
if(time>=0)
time--;
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
};
thread1.start();
//thread for controlling the bear eat pic appear time
Thread thread2 = new Thread() {
#Override
public void run() {
try {
while (true)
{
if (bearEating == true) {
sleep(200);
bearEating = false;
}
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
};
thread2.start();
}
#Override
protected void onDraw(Canvas canvas) {
// TODO Auto-generated method stub
super.onDraw(canvas);
if (time <= 0) {
Paint textPaint = new Paint();
textPaint.setColor(Color.BLACK);
textPaint.setTextAlign(Align.LEFT);
textPaint.setTextSize(65);
canvas.drawText(
"Game Over!",
canvas.getWidth() / 4, canvas.getHeight() / 2, textPaint);
canvas.drawText(
"Your Score is ",
canvas.getWidth() / 4, canvas.getHeight() / 2+65, textPaint);
canvas.drawText(
Integer.toString(totalscore),
canvas.getWidth() / 4, canvas.getHeight() / 2+130, textPaint);
} else {
checkbeareat = false;
canvasheight = canvas.getHeight();
canvaswidth = canvas.getWidth();
// draw score text
Paint textPaint = new Paint();
textPaint.setColor(Color.BLACK);
textPaint.setTextAlign(Align.LEFT);
textPaint.setTextSize(65);
canvas.drawText("Score: " + Integer.toString(totalscore), 50, 50,
textPaint);
canvas.drawText("Time left: " + Integer.toString(time), 50, 120,
textPaint);
// if have apple
if (appleCount != 0) {
for (int i = 0; i < appleCount; i++) {
if (appleList.get(i).checkstate()) {
// make every apple fall down
if ((appleList.get(i).getAppleY() + apple.getHeight() / 2) <= canvas
.getHeight()) {
appleList.get(i).setAppleY(
appleList.get(i).getAppleY()
+ appleList.get(i).getspeed());
} else {
// appleList.get(i).setAppleY(appleList.get(i).getAppleY());
applecombo = 0;
appleList.get(i).destoryApple();
}
// check if bear eat the apple
if (bearX + nomouthbear.getWidth() / 2 >= appleList
.get(i).getAppleX()
&& bearX <= appleList.get(i).getAppleX()
&& bearY>= appleList
.get(i).getAppleY()
&& bearY-nomouthbear.getHeight()/2 <= appleList.get(i).getAppleY()) {
// add score
totalscore += appleList.get(i).getAppleScore();
// change the state of apple to false so wont draw
// it
// again
appleList.get(i).destoryApple();
// draw bear ate
canvas.drawBitmap(nomouthbearget, bearX
- nomouthbear.getWidth() / 2, bearY
- nomouthbear.getHeight(), null);
checkbeareat = true;
bearEating = true;
applecombo++;
}
// draw apple
if (appleList.get(i).checkstate()) {
canvas.drawBitmap(
apple,
appleList.get(i).getAppleX()
- (apple.getWidth() / 2),
appleList.get(i).getAppleY()
- (apple.getHeight() / 2), null);
}
}
}
}
// draw bear
// canvas.drawBitmap(nomouthbear, 0, 0, null);
if (bearEating == false) {
if (bearX == 0 && bearY == 0){
canvas.drawBitmap(nomouthbear, 0, canvas.getHeight()- nomouthbear.getHeight()*2, null);}
else{
canvas.drawBitmap(nomouthbear,
bearX - nomouthbear.getWidth() / 2, bearY
- nomouthbear.getHeight(), null);}
} else {
canvas.drawBitmap(nomouthbearget,
bearX - nomouthbear.getWidth() / 2,
bearY - nomouthbear.getHeight(), null);
}
invalidate();
}
}
#Override
public boolean onTouchEvent(MotionEvent event) {
// TODO Auto-generated method stub
if (event.getX() >= nomouthbear.getWidth() / 2
&& event.getX() <= canvaswidth - nomouthbear.getWidth() / 2) {
bearX = event.getX();
} else if (event.getX() >= canvaswidth - nomouthbear.getWidth() / 2)
bearX = canvaswidth - nomouthbear.getWidth() / 2;
else
bearX = nomouthbear.getWidth() / 2;
bearY = canvasheight - nomouthbear.getHeight();
return true;
}
}
You shouldn't be calling invalidate from onDraw -- invalidate is what causes onDraw to be called. If you want the view to update itself periodically then you should call invalidate at some interval from your thread (e.g. Thread1) by posting a runnable to the view.
In terms of multithreading your code is completely unsafe (i.e. it is unpredictable) -- there is no synchronization or even use of volatiles.
What you are doing is only okay for the simplest of games, because it handles both rendering and computation in the UI thread, making it laggy when things get a bit more complicated.
You should be using a GLSurfaceView, check out its documentation and read up on OpenGL in Android
If you really do not want to use OpenGL, consider the regular SurfaceView, but be aware that it is a bit of a hassle to set up and it can't perform as smoothly.
You can create simply game using View and onDraw but it is very slowly and very unprofesional. You cant create good game using it. To create game you should use openGL (GLSurfaceView).
I recomend you book: J. F. DiMarzio Practical Android 4 Games Development It's good book to learn using OpenGL on Android.

Android Laggy onDraw() on Hexboard

I have been developing a board game using hexagon boards in android, but when I move the board, the whole 'map' or dozens of entities starts to lag. I believe that it is the cause of using a for-loop, but I have no idea how to fix it. Im using the method of threaded drawing, dont know if it affects though.
I can clearly see lags on my cell phone when moving through the board.
My Game class:
public class Game extends SurfaceView implements SurfaceHolder.Callback {
// Game Vars
int mapWidth = 150, mapHeight = 150;
public static double hexagonSideLength = 80;
public static double cellWidth = 2 * hexagonSideLength, cellHeight = Math
.sqrt(3) * hexagonSideLength;
public static double downwardShift = (0.5) * (cellHeight);
public static double rightShift = hexagonSideLength / 2;
public static int boundaryWidth = 0, boundaryHeight = 0;
public static int error = (int) ((hexagonSideLength) * (0.06));
public static int buffer = 2;
public static int draws = 0;
// Touch Handle
// Offset to the upper left corner of the map
private int xOffset = 0;
private int yOffset = 0;
// last touch point
private int _xTouch = 0;
private int _yTouch = 0;
// scrolling active?
private boolean _isMoving = false;
public Cell[][] map;
private GameThread thread;
static String TAG = Game.class.getSimpleName();
Bitmap image[] = new Bitmap[100];
Paint paint;
public Game(Context context) {
super(context);
Log.i(TAG, "Loaded Game");
// adding the callback (this) to the surface holder to intercept events
getHolder().addCallback(this);
// make the GamePanel focusable so it can handle events
thread = new GameThread(getHolder(), this);
map = new Cell[mapWidth][mapHeight]; // Create new Map
boolean isShiftedDownwards = false;
for (int i = 0; i < mapWidth; i++) {
for (int j = 0; j < mapHeight; j++) {
map[i][j] = new Cell(j, i, isShiftedDownwards);
}
if (isShiftedDownwards == true)
isShiftedDownwards = false;
else
isShiftedDownwards = true;
}
if (mapWidth % 2 != 0) {
boundaryWidth = (int) ((((mapWidth - 1) / 2) * hexagonSideLength)
+ ((mapWidth / 2) * cellWidth) + (2) * hexagonSideLength);
} else {
boundaryWidth = (int) (((((mapWidth - 1) / 2) * hexagonSideLength) + ((mapWidth / 2) * cellWidth)) + (1.5) * hexagonSideLength);
}
boundaryHeight = (int) (mapHeight * cellHeight + 0.5 * cellHeight);
setFocusable(true);
image[0] = Bitmap.createBitmap(BitmapFactory.decodeResource(
this.getResources(), R.drawable.hexagonrgb));
image[1] = Bitmap.createScaledBitmap(image[0], (int) cellWidth + error,
(int) cellHeight + error, false);
Log.i(TAG, "Got Resources");
paint = new Paint();
Log.i(TAG, "Prepared paint");
}
// called every Frame
#Override
protected void onDraw(Canvas canvas) {
Log.i(TAG, "onDraw Called");
// BG
canvas.drawColor(Color.BLACK);
// Resize
// Redraw Map
draws = 0;
for (int column = updateArea(0); column < updateArea(1); column++) {
for (int row = updateArea(2); row < updateArea(3); row++) {
canvas.drawBitmap(image[1], map[column][row].x - xOffset,
map[column][row].y - yOffset, paint);
paint.setColor(Color.WHITE);
canvas.drawText(row + "," + column, map[column][row].x
- xOffset + 80, map[column][row].y - yOffset + 80,
paint);
draws++;
}
}
/** Log */
paint.setColor(Color.WHITE);
paint.setTextSize(20);
canvas.drawText("xOffset: " + xOffset, 0, 30, paint);
canvas.drawText("yOffset: " + yOffset, 0, 50, paint);
canvas.drawText("activeTitlesX: " + updateArea(0) + " - "
+ updateArea(1), 0, 70, paint);
canvas.drawText("activeTitlesY: " + updateArea(2) + " - "
+ updateArea(3), 0, 90, paint);
canvas.drawText("DimX: " + MainActivity.DimX, 0, 110, paint);
canvas.drawText("DimY: " + MainActivity.DimY, 0, 130, paint);
canvas.drawText("Draws: " + draws, 0, 150, paint);
Log.i(TAG, "Cleared canvas");
}
#Override
public void onConfigurationChanged(Configuration newConfig) {
super.onConfigurationChanged(newConfig);
}
// called by thread
public static void update() {
Log.i(TAG, "Updated Game");
}
#Override
public void surfaceChanged(SurfaceHolder holder, int format, int width,
int height) {
// TODO Auto-generated method stub
}
#Override
public void surfaceCreated(SurfaceHolder holder) {
thread.setRunning(true);
thread.start();
Log.i(TAG, "Thread Started");
}
#Override
public void surfaceDestroyed(SurfaceHolder holder) {
boolean retry = true;
while (retry) {
try {
thread.join();
retry = false;
} catch (InterruptedException e) {
// try again shutting down the thread
}
}
Log.i(TAG, "Thread Destroyed");
}
#Override
public boolean onTouchEvent(MotionEvent event) {
// touch down
if (event.getAction() == MotionEvent.ACTION_DOWN) {
// start of a new event, reset the flag
_isMoving = false;
// store the current touch coordinates for scroll calculation
_xTouch = (int) event.getX();
_yTouch = (int) event.getY();
} else if (event.getAction() == MotionEvent.ACTION_MOVE) {
// touch starts moving, set the flag
_isMoving = true;
// get the new offset
xOffset += _xTouch - (int) event.getX();
yOffset += _yTouch - (int) event.getY();
// secure, that the offset is never out of view bounds
if (xOffset < 0) {
xOffset = 0;
} else if (xOffset > boundaryWidth - getWidth()) {
xOffset = boundaryWidth - getWidth();
}
if (yOffset < 0) {
yOffset = 0;
} else if (yOffset > boundaryHeight - getHeight()) {
yOffset = boundaryHeight - getHeight();
}
// store the last position
_xTouch = (int) event.getX();
_yTouch = (int) event.getY();
} else if (event.getAction() == MotionEvent.ACTION_UP) {
/*
* // touch released if (!_isMoving) { // calculate the touched cell
* int column = (int) Math.ceil((_xOffset + event.getX()) /
* _cellSize) - 1; int row = (int) Math.ceil((_yOffset +
* event.getY()) / _cellSize) - 1; Cell cell =
* _mapCells.get(row).get(column); // show the id of the touched
* cell Toast.makeText(getContext(), "Cell id #" + cell._id,
* Toast.LENGTH_SHORT).show(); }
*/
}
return true;
}
public int updateArea(int i) {
switch (i) {
case 0: // Left
return Math.max(
(int) (xOffset / (cellWidth - rightShift)) - buffer, 0);
case 1: // Right
return Math
.min((int) (xOffset / (cellWidth - rightShift) + (int) (MainActivity.DimX / (cellWidth - rightShift)))
+ buffer, mapWidth);
case 2: // Up
return Math.max((int) (yOffset / cellHeight) - buffer, 0);
case 3: // Down
return Math.min(((int) (yOffset / cellHeight))
+ (int) (MainActivity.DimY / cellHeight) + buffer,
mapHeight);
}
return 0;
}
/* CELL CLASS */
class Cell {
int x, y;
boolean isShiftedDown;
public Cell(int row, int col, boolean isShifted) {
if (col % 2 != 0) {
isShiftedDown = true;
y = (int) (row * Game.cellHeight + Game.downwardShift);
x = (int) (col * Game.cellWidth - col * Game.rightShift);
Log.i("Shift", "Shifted" + Game.downwardShift);
} else {
isShiftedDown = false;
y = (int) (row * Game.cellHeight);
x = (int) (col * Game.cellWidth - col * Game.rightShift);
Log.i("Shift", "Not Shifted");
}
}
}
}
Thanks in Advance :)
Basic algorithmic analysis shows you are doing this operation:
map[i][j] = new Cell(j, i, isShiftedDownwards);
22500 times. Do you really need to create a new object each time, or can you just change a variable in an existing object?
Update:
for (int column = updateArea(0); column < updateArea(1); column++) {
for (int row = updateArea(2); row < updateArea(3); row++) {
canvas.drawBitmap(image[1], map[column][row].x - xOffset,
map[column][row].y - yOffset, paint);
paint.setColor(Color.WHITE);
canvas.drawText(row + "," + column, map[column][row].x
- xOffset + 80, map[column][row].y - yOffset + 80,
paint);
draws++;
}
}
So this is the double for-loop in question. I'm going to run with the assumption that this needs to be run as many times as it runs. Can the function calls in the loop statements be reduced to variables prior to the loop (they are evaluated with each loop)? This paint.setColor(Color.WHITE); should not be called on each iteration. If you need to change to white after the very first draw, just make a second paint object on startup and and change it before you enter the loop.
If this doesn't help, you need to determine if you can iterate over a smaller area.
Update 2
Rewrite your for loops like so
int minCol = updateArea(0);
int maxCol = updateArea(1);
int minRow = updateArea(2);
int maxRow = updateArea(3);
for(int column = minCol; column < maxCol; column++)
for (int row = minRow; column < maxRow; row++)
....
This will represent a significant reduction in the operations in each iteration.

How to set-up automatic collision with just an update() function?

I've just started with Android programming using eclipse and recently came across this problem. I have a bunch of sprites assigned to an arraylist. Now, I want to make it so that collision is detected automatically between sprites but the template I'm currently using can only detect collision between the surface's borders and the moving sprites. Each sprite's position and speed is generated randomly.
How can I change the update() function in my Sprite() class to detect collision between the moving sprites themselves and at the same changing/bouncing to the opposite direction?
Here's my Sprite class template:
package com.gameproject.cai_test;
import java.util.Random;
public Sprite(GameView gameView, Bitmap bmp) {
this.width = bmp.getWidth() / BMP_COLUMNS;
this.height = bmp.getHeight() / BMP_ROWS;
this.gameView = gameView;
this.bmp = bmp;
Random rnd = new Random();
x = rnd.nextInt(gameView.getWidth() - width);
y = rnd.nextInt(gameView.getHeight() - height);
xSpeed = rnd.nextInt(MAX_SPEED * 2) - MAX_SPEED;
ySpeed = rnd.nextInt(MAX_SPEED * 2) - MAX_SPEED;
}
private void update() {
if (x >= gameView.getWidth() - width - xSpeed || x + xSpeed <= 0) {
xSpeed = -xSpeed;
}
x = x + xSpeed;
if (y >= gameView.getHeight() - height - ySpeed || y + ySpeed <= 0) {
ySpeed = -ySpeed;
}
y = y + ySpeed;
currentFrame = ++currentFrame % BMP_COLUMNS;
}
public void onDraw(Canvas canvas) {
update();
int srcX = currentFrame * width;
int srcY = getAnimationRow() * height;
Rect src = new Rect(srcX, srcY, srcX + width, srcY + height);
Rect dst = new Rect(x, y, x + width, y + height);
canvas.drawBitmap(bmp, src, dst, null);
}
private int getAnimationRow() {
double dirDouble = (Math.atan2(xSpeed, ySpeed) / (Math.PI / 2) + 2);
int direction = (int) Math.round(dirDouble) % BMP_ROWS;
return DIRECTION_TO_ANIMATION_MAP[direction];
}
//gameplay operations
//only values from 0 to 9 will be picked; each assigned its own sprite in the list
public int randomValue(){
Random rnd = new Random();
RandomValue = rnd.nextInt(10);
return RandomValue;
}
//sequence operation from addition, subtraction, multiplication, and division
public int produceSum(){
int addOne = 0;
int addTwo = 0;
Sum = addOne + addTwo;
return Sum;
}
public int produceDiff(){
int deductOne = 0;
int deductTwo = 0;
Difference = deductOne - deductTwo;
return Difference;
}
public int produceProduct(){
int multiOne = 0;
int multiTwo = 0;
Product = multiOne * multiTwo;
return Product;
}
public int produceQuotient(){
int divideOne = 0;
int divideTwo = 0;
Quotient = divideOne / divideTwo;
return Quotient;
}
//each time this returns true, the game is reset with new operation
//compares the value of the bubble picked to the random number being compared through operations
public boolean compareBubbleValue(int randomBubble, int bubbleValue){
if (randomBubble == bubbleValue){
return true;
}
return false;
}
}
As you can see, the update() method only checks the collision between the moving sprites and the borders.
Okey, so lets name your array of sprites spriteArray and loop through it twice
public Rect getBounds(){ //put this in your sprite and enemy-class.
return new Rect(x, y, x+width, y+height);
}
Public void checkCollision(){
for (int i = 0; i<spriteArray.size(); i++){
Rect mySprite = spriteArray.get(i).getBounds(); //create rect for every sprite in array
for (int j = 0; j<spriteArray.size(); i++){
Rect myOtherSprite = spriteArray.get(i).getBounds();
if(mySprite.intersect(myOtherSprite)){ //check if they touch
//Todo code here
}
}
}
}
then you just put this method in your update-method.
Here's the coding for the GameView class (pardon the mess, it's a jumble of codes and comments):
package com.gameproject.cai_test;
public class GameView extends SurfaceView {
private Bitmap bmp;
private Bitmap background;
private Bitmap backgroundImage;
private Bitmap pop;
private SurfaceHolder holder;
private GameLoopThread gameLoopThread;
private List<Sprite> sprites = new ArrayList<Sprite>();
private List<TempSprite> temps = new ArrayList<TempSprite>();
private int[] bubbleValue = {0,1,2,3,4,5,6,7,8,9,10};
private long lastClick;
private SpriteObject timer;
private SpriteObject morebanner;
private SpriteObject formulaBox;
private SpriteObject levelbanner1;
private SurfaceHolder surfaceHolder;
public GameView(Context context) {
super(context);
gameLoopThread = new GameLoopThread(this);
timer = new SpriteObject (BitmapFactory.decodeResource(getResources(), R.drawable.hourglass), 1200, 100);
morebanner = new SpriteObject(BitmapFactory.decodeResource(getResources(), R.drawable.morebanner), 650, 300);
formulaBox = new SpriteObject(BitmapFactory.decodeResource(getResources(), R.drawable.formulabox), 650, 600);
background = BitmapFactory.decodeResource(getResources(), R.drawable.background);
backgroundImage = BitmapFactory.decodeResource(getResources(), R.drawable.background);
pop = BitmapFactory.decodeResource(getResources(), R.drawable.pop);
final Toast toast1 = Toast.makeText(context, "LEVEL 1 start", Toast.LENGTH_LONG);
holder = getHolder();
holder.addCallback(new Callback() {
#Override
public void surfaceDestroyed(SurfaceHolder holder) {
}
#Override
public void surfaceCreated(SurfaceHolder holder) {
//setSurfaceSize(getWidth(), getHeight());
toast1.setGravity(Gravity.CENTER_VERTICAL, 0, 0);
toast1.show();
createSprites();
gameLoopThread.setRunning(true);
gameLoopThread.start();
//banner();
}
#Override
public void surfaceChanged(SurfaceHolder holder, int format,
int width, int height) {
}
});
}
private void createSprites() {
sprites.add(createSprite(R.drawable.bubble1));
sprites.add(createSprite(R.drawable.bubble2));
sprites.add(createSprite(R.drawable.bubble3));
sprites.add(createSprite(R.drawable.bubble4));
sprites.add(createSprite(R.drawable.bubble5));
sprites.add(createSprite(R.drawable.bubble6));
sprites.add(createSprite(R.drawable.bubble7));
sprites.add(createSprite(R.drawable.bubble8));
sprites.add(createSprite(R.drawable.bubble9));
sprites.add(createSprite(R.drawable.bubble10));
for (int i = 0; i <= 10; i++){
bubbleValue[i] = sprites.indexOf(i);
}
}
private Sprite createSprite(int resource) {
Bitmap bmp = BitmapFactory.decodeResource(getResources(), resource);
return new Sprite(this, bmp);
}
public void setSurfaceSize(int width, int height)
{
synchronized (surfaceHolder)
{
int canvasWidth = width;
int canvasHeight = height;
backgroundImage = Bitmap.createScaledBitmap(backgroundImage, width, height, true);
}
}
#Override
protected void onDraw(Canvas canvas) {
canvas.drawColor(Color.WHITE);
//levelbanner1.draw(canvas);//causes error when applied;for reference only
for (int i = temps.size() - 1; i >= 0; i--) {
temps.get(i).onDraw(canvas);
}
for (Sprite sprite : sprites) {
timer.draw(canvas);
//formulaBox.draw(canvas);
sprite.onDraw(canvas);
}
if (sprites.size() == 0){
morebanner.draw(canvas);
}
}
#Override
public boolean onTouchEvent(MotionEvent event) {
if (System.currentTimeMillis() - lastClick > 300) {
lastClick = System.currentTimeMillis();
float x = event.getX();
float y = event.getY();
synchronized (getHolder()) {
for (int i = sprites.size() - 1; i >= 0; i--) {
Sprite sprite = sprites.get(i);
if (sprite.isCollition(x, y)) {
sprites.remove(sprite);
temps.add(new TempSprite(temps, this, x, y, pop));
break;
}
}
}
}
return true;
}
public void update(){
//for possible check of collision bet. sprites
//
}
}
I've tried assigning a Rect for the sprites here and checking collision on the bottom update() function but result gets awry and produces a runtime error. Probably would be better if its automated in the Sprite class update() function, just as it does for border collision.
If you want to have nice and proper collisions between your sprites, maybe looking at a physics engine like http://www.jbox2d.org/ would help.
It will handle a lot of the complex specific cases for you (tunnelling, time of impact, broadphase for early discard...)

how manage background rendering?

I want to move background with the bob(Android Game Character) moving for that I make a Dynamic object name Background
public static int BACKGROUND_MOVE=0;
public static int BACKGROUND_FALL=1;
public static final float BACKGROUND_MOVE_VELOCITY =3.5f;
public static float BOB_WIDTH =10.0f;
public static float BOB_HEIGHT =15.0f;
public static long startTime = 0;
public int state;
public float stateTime;
public BackGround(float x, float y)
{
super(x, y, BOB_WIDTH, BOB_HEIGHT);
state = BACKGROUND_MOVE;
stateTime = 0;
accel.set(0,-2);
velocity.y=3.5f;//55
}
public void update (float deltaTime)
{
//velocity.add(World.gravity.x, World.gravity.y * deltaTime);
velocity.add(accel.x * deltaTime,accel.y*deltaTime);
position.add(velocity.x * deltaTime, velocity.y * deltaTime);
if (velocity.y > 0 && state==Bob.BOB_STATE_HIT)//BOB_STATE_HIT is bob running //condition
{
if (state != BACKGROUND_MOVE)
{
state = BACKGROUND_MOVE;
stateTime = 0;
}
}
if (velocity.y > 0 && state != Bob.BOB_STATE_HIT)
{
if (state != BACKGROUND_FALL)
{
state = BACKGROUND_FALL;
stateTime = 0;
}
}
// if (velocity.y < 0 && state == BOB_STATE_HIT)
// {
// if (state != BOB_STATE_JUMP) {
// state = BOB_STATE_JUMP;
// stateTime = 0;
// }
//}
//if (position.y < 0) position.x = World.WORLD_WIDTH;
//if (position.x > World.WORLD_WIDTH) position.x = 0;
stateTime += deltaTime;
}
public void move()
{
if(state==BACKGROUND_MOVE)
{
startTime=System.nanoTime()/1000000000;
// state = BACKGROUND_MOVE;
velocity.y = BACKGROUND_MOVE_VELOCITY;
stateTime = 0;
}
}
similar to bob I amke object in World class and make an ArrayList Add Backgroung into that arraylist and at time of drawing get from arraylist and draw it...but no any effect show simply screen show and cross the rangeand red screen shown...please anyone help..
/*---------------background cam------*/
this.bcam = new OrthographicCamera(FRUSTUM_WIDTH,FRUSTUM_HEIGHT);
this.bcam.position.set(FRUSTUM_WIDTH,FRUSTUM_HEIGHT, 0);
this.batch = batch;
public void renderBackground()
{
GLCommon gl = Gdx.gl;
gl.glClearColor(1, 0, 0, 1);
gl.glClear(GL10.GL_COLOR_BUFFER_BIT);
bcam.update();
batch.setProjectionMatrix(bcam.combined);
batch.disableBlending();
batch.begin();
if (world.objbackground.state ==BackGround.BACKGROUND_MOVE)
batch.draw(Assets.mainbackgroundRegion, cam.position.x - FRUSTUM_WIDTH / 2, cam.position.y - FRUSTUM_HEIGHT / 2, FRUSTUM_WIDTH, FRUSTUM_HEIGHT);
// else
// {
// batch.draw(Assets.touchbackgroundRegion, cam.position.x -
// FRUSTUM_WIDTH / 2, cam.position.y - FRUSTUM_HEIGHT / 2,
// FRUSTUM_WIDTH, FRUSTUM_HEIGHT);
// if (elapsed == 5)
// changebackground = 0;
// }
batch.end();
}
Either you want to move the background with Bob (1) or you want to have a fixed background (2):
(1): Set background's position to Bob's (probably with an offset, you can do that in the update function of the world), render using batch.draw(......, background.position.x, background.position.y).
(2): Throw away your complicated update methods in the BackGround object and use the render method as you do now.
But honestly, your intentions and explanations are not very clear, try to improve on that!

How to fix the position of image on a scrollable background?

How do you fix the position of an image drawn onto a canvas with a large scrollable background?
picture this:
the background image is that of a room, the object images are bed, door etc.
the object images are drawn on top of the background.
when i scroll the background, the objects should move with respect to the background image, correct?
the problem is the object images also move but the position doesn't stay the same, i.e they shift from their original positions.
here is my full class implementation.
Bitmap bmImage;
SpriteAnim8 anim;
MThread thread;
PersonAnimated person, person2;
Canvas canvas = new Canvas();
Rect displayRect = null;
Rect scrollRect = null;
int scrollRectX = 0, scrollRectY = 0;
float scrollByX = 0, scrollByY = 0;
float startX = 0, startY = 0;
int initX = 200, initY = 200;
float a = initX, b = initY;
public MGamePanel(Context context) {
super(context);
// adding the callback (this) to the surface holder to intercept events
getHolder().addCallback(this);
// create Person and load bitmap
person = new PersonAnimated(BitmapFactory.decodeResource(getResources(), R.drawable.dad_anim),
10, 200 /* initial position */,
45, 56 /* width and height of sprite */,
5, 10); /* FPS and number of frames in the animation */
// Destination rect for our main canvas draw
displayRect = new Rect(0, 0, SpriteAnim8.displayWidth, SpriteAnim8.displayHeight);
// Scroll rect: this will be used to 'scroll around' over the bitmap
scrollRect = new Rect(0, 0, SpriteAnim8.displayWidth, SpriteAnim8.displayHeight);
// Load a large bitmap
bmImage = BitmapFactory.decodeResource(getResources(), R.drawable.l1_plain);
// create the game loop thread
thread = new MThread(getHolder(), this);
// make the GamePanel focusable so it can handle events
setFocusable(true);
}
#Override
public void surfaceChanged(SurfaceHolder holder, int format, int width,
int height) {
}
#Override
public void surfaceCreated(SurfaceHolder holder) {
// at this point the surface is created and we can safely start the game
// loop
thread.setRunning(true);
thread.start();
}
#Override
public void surfaceDestroyed(SurfaceHolder holder) {
Log.d(TAG, "Surface is being destroyed");
// tell the thread to shut down and wait for it to finish
// this is a clean shutdown
boolean retry = true;
while (retry) {
try {
thread.setRunning(false);
((Activity) getContext()).finish();
retry = false;
} catch (Exception e) {
// try again shutting down the thread
}
}
Log.d(TAG, "Thread was shut down cleanly");
}
#Override
public boolean onTouchEvent(MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
// delegating event handling to the person
startX = event.getRawX();
startY = event.getRawY();
// }
case MotionEvent.ACTION_MOVE:
float x = event.getRawX();
float y = event.getRawY();
// Calculate move update.
mScrollByX = x - mStartX + mOldScrollByX; // move update x increment
mScrollByY = y - mStartY + mOldScrollByX; // move update y increment
onDraw(canvas);
break;
case MotionEvent.ACTION_UP:
mOldScrollByX = mScrollByX;
mOldScrollByY = mScrollByY;
break;
}
return true;
}
public void render(Canvas canvas) {
Paint paint = new Paint();
canvas.drawBitmap(bmImage, scrollRect, displayRect, paint);
person.draw(canvas);
}
#Override
protected void onDraw(Canvas canvas) {
int newScrollRectX = scrollRectX - (int) scrollByX;
int newScrollRectY = scrollRectY - (int) scrollByY;
// Prevent scrolling off the left or right edges of the bitmap.
if (newScrollRectX < 0) {
newScrollRectX = 0;
} else if (newScrollRectX > (bmImage.getWidth() - SpriteAnim8.displayWidth)) {
newScrollRectX = (bmImage.getWidth() - SpriteAnim8.displayWidth);
}
// Prevent scrolling off the top or bottom edges of the bitmap.
if (newScrollRectY < 0) {
newScrollRectY = 0;
} else if (newScrollRectY > (bmImage.getHeight() - SpriteAnim8.displayHeight)) {
newScrollRectY = (bmImage.getHeight() - SpriteAnim8.displayHeight);
}
// set the updated scroll rect coordinates.
scrollRect.set(newScrollRectX, newScrollRectY, newScrollRectX
+ SpriteAnim8.displayWidth, newScrollRectY
+ SpriteAnim8.displayHeight);
// Reset current scroll coordinates to reflect the latest updates so we
// can repeat
scrollRectX = newScrollRectX;
scrollRectY = newScrollRectY;
person.setX(person.getX() + scrollByX);
person.setY(person.getY() + scrollByY);
}
is this correct?
Here is a code how to scroll a picture/image which is drawn on canvas:
//define points on the custome view class level
PointF touchStart = new PointF();
PointF picStart = new PointF();
PointF prevPicStart = new PointF();
Handle the touch and remember the previous poistion of the picture
#Override
public boolean onTouchEvent(MotionEvent ev) {
switch (ev.getAction()) {
case MotionEvent.ACTION_DOWN: {
touchStart.set(ev.getX(), ev.getY());
break;
}
case MotionEvent.ACTION_MOVE: {
float newX = ev.getX() - touchStart.x + prevPicStart.x;
float newY = ev.getY() - touchStart.y + prevPicStart.y;
//assuming the the picture is bigger than the screen
if ((newX <= 0 && newX > 0 - pic.getWidth() + screenW)){
picStart.x = newX;
}
if ((newY <= 0 && newY > 0 - pic.getHeight() + screenH)){
picStart.y = newY;
}
invalidate();
break;
}
case MotionEvent.ACTION_UP:
prevPicStart.x = picStart.x;
prevPicStart.y = picStart.y;
break;
}
return true;
}
In onDraw
canvas.drawBitmap(pic, picStart.x, picStart.y, null);
Not sure about your code, but you can use a FrameLayout with background that you want for room and add ScrollView as its child, which will contain the other objects which are scrollable.
HTH !

Categories

Resources