I am trying to make a top down two player shooter and am currently trying to implement a system for the character to shoot his gun multiple times, but currently overtime he changes direction, the bullets change direction. I know why this occurs I just have no idea how to get around this issue. I need the bullets in the iterator to move independently of the next bullet. I am also using LibGdx to accomplish thins in which the render method is called every frame
<
package com.mygdx.game;
import com.badlogic.gdx.ApplicationAdapter;
import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.Input;
import com.badlogic.gdx.graphics.GL20;
import com.badlogic.gdx.graphics.Texture;
import com.badlogic.gdx.graphics.g2d.SpriteBatch;
import com.badlogic.gdx.graphics.g2d.TextureRegion;
import com.badlogic.gdx.math.MathUtils;
import com.badlogic.gdx.math.Rectangle;
import com.badlogic.gdx.utils.Array;
import com.badlogic.gdx.utils.TimeUtils;
import java.util.Iterator;
public class MyGdxGame extends ApplicationAdapter {
SpriteBatch batch;
Texture bluePlayerTexture, redPlayerTexture,bulletTexture;
Rectangle bluePlayer,redPlayer;
TextureRegion[] regions = new TextureRegion[2];
TextureRegion[] bulletRegions = new TextureRegion[1];
private int blueRotation = 0,redRotation = 180,bulletSpeed = 1;
private float xRedBulletSpeed, yRedBulletSpeed;
private Array redBullets;
private long lastShotTime;
@override
public void create () {
batch = new SpriteBatch();
bluePlayerTexture = new Texture("blue.png");
redPlayerTexture = new Texture("red.png");
bulletTexture = new Texture("bullet.png");
regions[0] = new TextureRegion(bluePlayerTexture, 0f,0f,1f,1f);
regions[1] = new TextureRegion(redPlayerTexture, 0f,0f,1f,1f);
bulletRegions[0] = new TextureRegion(bulletTexture, 0f,0f,1f,1f);
createRectangles();
redBullets = new Array<Rectangle>();
spawnRedBullet();
}
@override
public void render () {
Gdx.gl.glClearColor(1, 1, 1, 1);
Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);
setBulletRedVelocity();
batch.begin();
redPlayerInput(batch);
bluePlayerInput(batch);
for(Rectangle redBullet: redBullets) {
batch.draw(bulletRegions[0], redBullet.x, redBullet.y, redBullet.width / 2, redBullet.height / 2, redBullet.width, redBullet.height, 1, 1, redRotation);
}
batch.draw(regions[0], bluePlayer.x, bluePlayer.y, bluePlayer.width / 2, bluePlayer.height / 2, bluePlayer.width, bluePlayer.height, 1, 1, blueRotation);
batch.draw(regions[1], redPlayer.x, redPlayer.y, redPlayer.width / 2, redPlayer.height / 2, redPlayer.width, redPlayer.height, 1, 1, redRotation);
batch.end();
Iterator<Rectangle> iter = redBullets.iterator();
while(iter.hasNext()) {
Rectangle redBullet = iter.next();
if(redRotation == 90) {
redBullet.y += 200 * Gdx.graphics.getDeltaTime();
}
if(redRotation == 180) {
redBullet.x -= 200 * Gdx.graphics.getDeltaTime();
}
if(redRotation == 0){
redBullet.x += 200 * Gdx.graphics.getDeltaTime();
}
if(redRotation == 270){
redBullet.y -= 200 * Gdx.graphics.getDeltaTime();
}
}
}
private void spawnRedBullet() {
Rectangle redBullet = new Rectangle();
redBullet.x = redPlayer.x;
redBullet.y = redPlayer.y;
redBullet.width = 20;
redBullet.height = 12;
redBullets.add(redBullet);
lastShotTime = TimeUtils.nanoTime();
}
public void createRectangles(){
bluePlayer = new Rectangle();
bluePlayer.width = 64;
bluePlayer.height = 64;
bluePlayer.x = Gdx.graphics.getWidth()/4-bluePlayer.width/2;
bluePlayer.y = Gdx.graphics.getHeight()/2-bluePlayer.width/2;
redPlayer = new Rectangle();
redPlayer.width = 64;
redPlayer.height = 64;
redPlayer.x = (Gdx.graphics.getWidth()/4)*3-redPlayer.width/2;
redPlayer.y = Gdx.graphics.getHeight()/2-redPlayer.width/2;
}
public void redPlayerInput(SpriteBatch batch){
//if(Gdx.input.isKeyPressed(Input.Keys.SHIFT_LEFT)) {
//redRotation+=2;
//}
//if(Gdx.input.isKeyPressed(Input.Keys.CONTROL_LEFT)){
// redRotation-=2;
//}
if(Gdx.input.isKeyPressed(Input.Keys.W)){
redRotation = 90;
redPlayer.y+=2;
}
if(Gdx.input.isKeyPressed(Input.Keys.S)){
redRotation = 270;
redPlayer.y-=2;
}
if(Gdx.input.isKeyPressed(Input.Keys.A)){
redRotation = 180;
redPlayer.x-=2;
}
if(Gdx.input.isKeyPressed(Input.Keys.D)){
redRotation = 0;
redPlayer.x+=2;
}
if(Gdx.input.isKeyPressed(Input.Keys.E)){
if(TimeUtils.nanoTime() - lastShotTime > 500000000) spawnRedBullet();
}
}
public void bluePlayerInput(SpriteBatch batch){
//if(Gdx.input.isKeyPressed(Input.Keys.SHIFT_RIGHT)) {
// blueRotation-=2;
//}
//if(Gdx.input.isKeyPressed(Input.Keys.ENTER)){
// blueRotation+=2;
//}
if(Gdx.input.isKeyPressed(Input.Keys.P)){
bluePlayer.y+=2;
}
if(Gdx.input.isKeyPressed(Input.Keys.SEMICOLON)){
bluePlayer.y-=2;
}
if(Gdx.input.isKeyPressed(Input.Keys.L)){
bluePlayer.x-=2;
}
if(Gdx.input.isKeyPressed(Input.Keys.APOSTROPHE)){
bluePlayer.x+=2;
}
if(Gdx.input.isKeyPressed(Input.Keys.O)){
//fire gun
}
}
public void setBulletRedVelocity(){
xRedBulletSpeed = (float) (Math.cos(redRotation)*bulletSpeed);
yRedBulletSpeed = (float) (Math.sin(redRotation)*bulletSpeed);
}
>
Related
Hey fellas, i have just started learning android developing. But i am having some problem in understanding the concept of animation.
While learning some android game development, i stumbled upon this sweet piece of code! I am able to understand 3/4th of it. But still i want to learn it fully. So ill just paste the code. And the highlight some of my problems. Ill be very glad if you people help me.
It is a kind of game, which basically throws a greenball, with the users swype. Ill be pasting the class here..
Code:
package com.example.done;
import android.app.Activity;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.os.Bundle;
import android.view.MotionEvent;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
import android.view.View;
import android.view.View.OnTouchListener;
public class GFXsurface extends Activity implements OnTouchListener{
MyBringBackSurface ourSurfaceView;
float x, y, sX, sY, fX, fY, dX, dY, aniX, aniY, scaledX, scaledY;//ani for animaton
Bitmap test, plus;
[user=439709]@override[/user]
protected void onCreate(Bundle savedInstanceState) {
// TODO Auto-generated method stub
super.onCreate(savedInstanceState);
ourSurfaceView = new MyBringBackSurface(this);
ourSurfaceView.setOnTouchListener(this);
x = 0;
y = 0;
sX = 0;
sY = 0;
fX = 0;
fY = 0;
dX = dY = aniX = aniY = scaledX = scaledY = 0; //they are equal to 0
test = BitmapFactory.decodeResource(getResources(), R.drawable.greenball);
plus = BitmapFactory.decodeResource(getResources(), R.drawable.plus);
setContentView(ourSurfaceView);
}
[user=439709]@override[/user]
protected void onPause() {
// TODO Auto-generated method stub
super.onPause();
ourSurfaceView.pause();
}
[user=439709]@override[/user]
protected void onResume() {
// TODO Auto-generated method stub
super.onResume();
ourSurfaceView.resume();
}
public boolean onTouch(View v, MotionEvent event) {
// TODO Auto-generated method stub
try {
Thread.sleep(50);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
x = event.getX();
y = event.getY();
switch(event.getAction()){
case MotionEvent.ACTION_DOWN:
sX = event.getX();
sY = event.getY();
dX = dY = aniX = aniY = scaledX = scaledY = fX = fY = 0;
break;
case MotionEvent.ACTION_UP:
fX = event.getX();
fY = event.getY();
dX = fX-sX; //dx is the change in direction final x- starting x
dY = fY-sY;
scaledX = dX/30;
scaledY = dY/30;
x = y =0;
break;
}
return true;
}
public class MyBringBackSurface extends SurfaceView implements Runnable {
SurfaceHolder ourHolder;
Thread ourThread = null;
boolean isRunning = false;
public MyBringBackSurface(Context context) {
super(context);
ourHolder = getHolder();
}
public void pause(){
isRunning = false;
while(true){
try {
ourThread.join();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
break;
}
ourThread = null;
}
public void resume(){
isRunning = true;
ourThread = new Thread(this);
ourThread.start();
}
public void run() {
// TODO Auto-generated method stub
while(isRunning){
if(!ourHolder.getSurface().isValid())
continue;
Canvas canvas = ourHolder.lockCanvas();
canvas.drawRGB(02, 02, 150);
if (x != 0 && y != 0){
canvas.drawBitmap(test, x-(test.getWidth()/2), y-(test.getHeight()/2), null);
}
if (sX != 0 && sY != 0){
canvas.drawBitmap(plus, sX-(plus.getWidth()/2), sY-(plus.getHeight()/2), null);
}
if (fX != 0 && fY != 0){
canvas.drawBitmap(test, fX-(test.getWidth()/2)-aniX, fY-(test.getHeight()/2)-aniY, null);
canvas.drawBitmap(plus, fX-(plus.getWidth()/2), fY-(plus.getHeight()/2), null);
}
aniX = aniX + scaledX;
aniY = aniY + scaledY;
ourHolder.unlockCanvasAndPost(canvas);
}
}
}
}
This are some of the codes, whose purpose are unknown to me:
Code:
scaledX = dX/30;
scaledY = dY/30; //why is this being divided by 30?
_________________________________
Code:
aniX = aniX + scaledX;
aniY = aniY + scaledY;
___________________________
I can you please give me an overview of the whole code.
How the whole stuff is working in order.
Thanks in advance!
The animation seems to be done in 30 steps. The movement has a constant speed. Each step the picture or whatever is moved the difference between fX and sX divided by 30. So the movement is done slowly. If there were no division by 30, it would appear at the end location without any animation.
---------- Post added at 04:36 PM ---------- Previous post was at 04:23 PM ----------
hiphop12ism said:
Hey fellas, i have just started learning android developing. But i am having some problem in understanding the concept of animation.
While learning some android game development, i stumbled upon this sweet piece of code! I am able to understand 3/4th of it. But still i want to learn it fully. So ill just paste the code. And the highlight some of my problems. Ill be very glad if you people help me.
It is a kind of game, which basically throws a greenball, with the users swype. Ill be pasting the class here..
Code:
package com.example.done;
import android.app.Activity;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.os.Bundle;
import android.view.MotionEvent;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
import android.view.View;
import android.view.View.OnTouchListener;
public class GFXsurface extends Activity implements OnTouchListener{
MyBringBackSurface ourSurfaceView;
float x, y, sX, sY, fX, fY, dX, dY, aniX, aniY, scaledX, scaledY;//ani for animaton
Bitmap test, plus;
[user=439709]@override[/user]
protected void onCreate(Bundle savedInstanceState) {
// TODO Auto-generated method stub
super.onCreate(savedInstanceState);
ourSurfaceView = new MyBringBackSurface(this);
ourSurfaceView.setOnTouchListener(this);
x = 0;
y = 0;
sX = 0;
sY = 0;
fX = 0;
fY = 0;
dX = dY = aniX = aniY = scaledX = scaledY = 0; //they are equal to 0
test = BitmapFactory.decodeResource(getResources(), R.drawable.greenball); //converts the drawables to Bitmaps
plus = BitmapFactory.decodeResource(getResources(), R.drawable.plus);
setContentView(ourSurfaceView);
}
[user=439709]@override[/user]
protected void onPause() {
// TODO Auto-generated method stub
super.onPause();
ourSurfaceView.pause();
}
[user=439709]@override[/user]
protected void onResume() {
// TODO Auto-generated method stub
super.onResume();
ourSurfaceView.resume();
}
public boolean onTouch(View v, MotionEvent event) { //called when the screen is touched or the finger is lifted
// TODO Auto-generated method stub
try {
Thread.sleep(50);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
x = event.getX(); //x and y coordinates of the screen
y = event.getY();
switch(event.getAction()){
case MotionEvent.ACTION_DOWN: //when the finger is touching the screen
sX = event.getX();
sY = event.getY();
dX = dY = aniX = aniY = scaledX = scaledY = fX = fY = 0;
break;
case MotionEvent.ACTION_UP: //when the finger is lifted
fX = event.getX();
fY = event.getY();
dX = fX-sX; //dx is the change in direction final x- starting x
dY = fY-sY;
scaledX = dX/30; the way the bitmap moves each time interval
scaledY = dY/30;
x = y =0;
break;
}
return true;
}
public class MyBringBackSurface extends SurfaceView implements Runnable {
SurfaceHolder ourHolder;
Thread ourThread = null;
boolean isRunning = false;
public MyBringBackSurface(Context context) {
super(context);
ourHolder = getHolder();
}
public void pause(){
isRunning = false;
while(true){
try {
ourThread.join();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
break;
}
ourThread = null;
}
public void resume(){
isRunning = true;
ourThread = new Thread(this);
ourThread.start();
}
public void run() {
// TODO Auto-generated method stub
while(isRunning){
if(!ourHolder.getSurface().isValid())
continue;
Canvas canvas = ourHolder.lockCanvas(); //prepares the Canvas for drawing
canvas.drawRGB(02, 02, 150); //fills the canvas with the given color
if (x != 0 && y != 0){
canvas.drawBitmap(test, x-(test.getWidth()/2), y-(test.getHeight()/2), null); //draws the bitmap test at the given coordinates
}
if (sX != 0 && sY != 0){
canvas.drawBitmap(plus, sX-(plus.getWidth()/2), sY-(plus.getHeight()/2), null);
}
if (fX != 0 && fY != 0){
canvas.drawBitmap(test, fX-(test.getWidth()/2)-aniX, fY-(test.getHeight()/2)-aniY, null);
canvas.drawBitmap(plus, fX-(plus.getWidth()/2), fY-(plus.getHeight()/2), null);
}
aniX = aniX + scaledX; //x coordinate is changed by the size of the step
aniY = aniY + scaledY;
ourHolder.unlockCanvasAndPost(canvas); //shows the canvas as the content of the SurfaceView
}
}
}
}
This are some of the codes, whose purpose are unknown to me:
Code:
scaledX = dX/30;
scaledY = dY/30; //why is this being divided by 30?
_________________________________
Code:
aniX = aniX + scaledX;
aniY = aniY + scaledY;
___________________________
I can you please give me an overview of the whole code.
How the whole stuff is working in order.
Thanks in advance!
Click to expand...
Click to collapse
I modified your code. Will add more explanations later.
---------- Post added at 04:42 PM ---------- Previous post was at 04:36 PM ----------
I am ready with adding comments.
Look at the comments in the code above.
Thank you for being so generous.
Its probably gettimg more clear to me.
Sent from my GT-S6102 using xda app-developers app
hi, its killing me i can't fix it i made this thing because i was learning Canvas and drawing on android !
i included moving animations (smooth) but i removed it
Code:
package com.example.graphics;
import android.content.Context;
import android.view.KeyEvent;
import android.view.MotionEvent;
import java.util.Formatter;
import android.graphics.Typeface;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.RectF;
import android.view.View;
public class BouncingBallView extends View {
private int xMin = 0; // This view's bounds
private int xMax;
private int yMin = 0;
private int yMax;
private float ballRadius = 80; // Ball's radius
private float ballX = ballRadius + 20; // Ball's center (x,y)
private float ballY = ballRadius + 40;
private float ballSpeedX = 11; // Ball's speed (x,y)
private float ballSpeedY = 7;
//private RectF ballBounds; // Needed for Canvas.drawOval
private Paint paint; // The paint (e.g. style, color) used for drawing
// Status message to show Ball's (x,y) position and speed.
private StringBuilder statusMsg = new StringBuilder();
private Formatter formatter = new Formatter(statusMsg); // Formatting the statusMsg
private float previousX;
private float previousY;
private float currentX;
private float currentY;
private float scale;
private int ifdrawcount = 0;
// Constructor
public BouncingBallView(Context context) {
super(context);
//ballBounds = new RectF();
paint = new Paint();
paint.setTypeface(Typeface.MONOSPACE);
paint.setTextSize(25);
setFocusableInTouchMode(true);
//setFocusable(true);
requestFocus();
}
/*public boolean onKeyUp(int keyCode, KeyEvent event) {
switch (keyCode) {
case KeyEvent.KEYCODE_DPAD_RIGHT: // Increase rightward speed
ballSpeedX++;
break;
case KeyEvent.KEYCODE_DPAD_LEFT: // Increase leftward speed
ballSpeedX--;
break;
case KeyEvent.KEYCODE_DPAD_UP: // Increase upward speed
ballSpeedY--;
break;
case KeyEvent.KEYCODE_DPAD_DOWN: // Increase downward speed
ballSpeedY++;
break;
case KeyEvent.KEYCODE_DPAD_CENTER: // Stop
ballSpeedX = 0;
ballSpeedY = 0;
break;
case KeyEvent.KEYCODE_A: // Zoom in
// Max radius is about 90% of half of the smaller dimension
float maxRadius = (xMax > yMax) ? yMax / 2 * 0.9f : xMax / 2 * 0.9f;
if (ballRadius < maxRadius) {
ballRadius *= 1.05; // Increase radius by 5%
}
break;
case KeyEvent.KEYCODE_Z: // Zoom out
if (ballRadius > 20) { // Minimum radius
ballRadius *= 0.95; // Decrease radius by 5%
}
break;
}
return true; // Event handled
}*/
// Called back to draw the view. Also called by invalidate().
@Override
protected void onDraw(Canvas canvas) {
// Draw the ball
//ballBounds.set(ballX-ballRadius, ballY-ballRadius, ballX+ballRadius, ballY+ballRadius);
//paint.setColor(Color.GRAY);
//canvas.drawOval(ballBounds, paint);
//canvas.drawCircle(70, yMax -70, 60, paint);
//canvas.drawCircle(xMax -70, yMax -60, 60, paint);
paint.setColor(Color.GREEN);
canvas.drawCircle(ballX, ballY, ballRadius, paint);
// Draw the status message
paint.setColor(Color.WHITE);
paint.setStrokeWidth(2);
canvas.drawText(statusMsg.toString(), 10, 30, paint);
if(ifdrawcount > 0){
canvas.drawLine(previousX, previousY, currentX, currentY, paint);
paint.setStrokeWidth(10);
paint.setColor(Color.BLACK);
canvas.drawPoint(previousX,previousY,paint);
ifdrawcount--;
}
// Update the position of the ball, including collision detection and reaction.
update();
// Delay
try {
Thread.sleep(16);
} catch (InterruptedException e) { }
invalidate(); // Force a re-draw
}
// Touch-input handler
@Override
public boolean onTouchEvent(MotionEvent event) {
currentX = event.getX();
currentY = event.getY();
ifdrawcount = 10;
//float deltaX, deltaY;
scale = 20.0f / ((xMax > yMax) ? yMax : xMax);
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
previousX = currentX;
previousY = currentY;
case MotionEvent.ACTION_MOVE:
ballSpeedY = (currentY - previousY) * scale;
ballSpeedX = (currentX - previousX) * scale;
case MotionEvent.ACTION_UP:
ballSpeedX = 0;
ballSpeedY = 0;
}
return true; // Event handled
}
// Detect collision and update the position of the ball.
private void update() {
// Get new (x,y) position
ballX += ballSpeedX;
ballY += ballSpeedY;
/*if(ifdrawcount == 0)
ballSpeedX = 0; ballSpeedY = 0;*/
// Detect collision and react
if (ballX + ballRadius > xMax) {
ballSpeedX = -ballSpeedX;
ballX = xMax-ballRadius;
} else if (ballX - ballRadius < xMin) {
ballSpeedX = -ballSpeedX;
ballX = xMin+ballRadius;
}
if (ballY + ballRadius > yMax) {
ballSpeedY = -ballSpeedY;
ballY = yMax - ballRadius;
} else if (ballY - ballRadius < yMin) {
ballSpeedY = -ballSpeedY;
ballY = yMin + ballRadius;
}
// Build status message
statusMsg.delete(0, statusMsg.length()); // Empty buffer
formatter.format("%3.0f %3.0f || %3.0f %3.0f", ballSpeedX, ballSpeedY,ballX,ballY);
}
// Called back when the view is first created or its size changes.
@Override
public void onSizeChanged(int w, int h, int oldW, int oldH) {
// Set the movement bounds for the ball
xMax = w-1;
yMax = h-1;
}
}
the problem is my ball doesn't move at all, but i see the line, once i remove the line:
Code:
case MotionEvent.ACTION_UP:
ballSpeedX = 0;
ballSpeedY = 0;
it works, but you know, after release it continues with same speed :! its like the ACTION_UP is always the case, !!! any idea why it doesn't work, how to stop the ball once the user releases the screen?
also sorry if this is not the right place. i searched the forums, i got confused i didn't find anyplace to ask this question so i tough this is the best place!
I have an android application that is receiving a string from an arduino via Bluetooth, names the string "data" and displays it by setting a TextView to the string "data". I want a chronometer to start when the incoming string matches a predefined string.
For example:
Code:
if data.equals(startChrono)){
chronometerLeft.setBase(SystemClock.elapsedRealtime());
chronometerLeft.start();
I actually have the arduino sending a "g" and am setting my string goL to be "g" but cannot get the chronometer to start when the g is received. My TextView shows the g. Code is below. I've tried several things and at a loss. Using same code for chronometer.start() with onClickListener with a button works great. I just need it to start the chronometer when i receive a specific string from the arduino.
Code:
beginListenForData();
// text.setText("Bluetooth Opened");
}
void beginListenForData() {
final Handler handler = new Handler();
final byte delimiter = 10; // This is the ASCII code for a newline
// character
stopWorker = false;
readBufferPosition = 0;
readBuffer = new byte[1024];
workerThread = new Thread(new Runnable() {
public void run() {
while (!Thread.currentThread().isInterrupted() && !stopWorker) {
try {
int bytesAvailable = mmInputStream.available();
if (bytesAvailable > 0) {
byte[] packetBytes = new byte[bytesAvailable];
mmInputStream.read(packetBytes);
for (int i = 0; i < bytesAvailable; i++) {
byte b = packetBytes[i];
if (b == delimiter) {
byte[] encodedBytes = new byte[readBufferPosition];
System.arraycopy(readBuffer, 0,
encodedBytes, 0,
encodedBytes.length);
final String data = new String(
encodedBytes, "US-ASCII");
readBufferPosition = 0;
handler.post(new Runnable() {
public void run() {
text.setText(data);
String goL = "g";
String goR = "f";
chronometerLeft = (Chronometer)findViewById(R.id.chronometerLeft);
chronometerRight = (Chronometer)findViewById(R.id.chronometerRight);
if(data.equals(goL)){
chronometerLeft.setBase(SystemClock.elapsedRealtime());
chronometerLeft.start();
if(data.equals(goR))
chronometerRight.setBase(SystemClock.elapsedRealtime());
chronometerRight.start();
}
}
});
} else {
readBuffer[readBufferPosition++] = b;
}
}
}
} catch (IOException ex) {
stopWorker = true;
}
}
}
});
workerThread.start();
}
Sorry to bother, but in your while loop condition, what does the '!' before Thread do?
As what was stated on the header I want to implement either a "paint" function for user to edit paint/censor unwanted parts of a photo displayed on a imageview before uploading it to a server in the edited format and a redo function if user makes a mistake while editing?
How do I come about doing it, I've read relevant topics on Canvas, or FingerPaint but still puzzled on how to implement it based on my project here? Tried referencing to the links here and here but without success in implementing the codes into my project code due to my lack of programming skills.
Thanks for any help rendered!
Tried integrating the codes below into my code above (image preview after taking a photo with the camera) for user to start editing via painting but still not working? Thanks for any help rendered!
Code:
public class Drawing extends View {
private Paint mPaint, mBitmapPaint;
Intent intent = getIntent();
Bitmap mBitmap = (Bitmap) intent.getParcelableExtra("BitmapImage");
private Canvas mCanvas;
private Path mPath;
private float mX, mY;
private static final float TOUCH_TOLERANCE = 4;
private int color, size, state;
private ArrayList<Path> paths = new ArrayList<Path>();
private ArrayList<Path> undonePaths = new ArrayList<Path>();
private ArrayList<Integer> colors = new ArrayList<Integer>();
private ArrayList<Integer> sizes = new ArrayList<Integer>();
public Drawing(Context c) {
super(c);
}
public Drawing(Context c,int width, int height, int size, int color, int state) {
super(c);
mBitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
mCanvas = new Canvas(mBitmap);
mPath = new Path();
mPaint = new Paint();
mPaint.setAntiAlias(true);
mPaint.setDither(true);
mPaint.setStyle(Paint.Style.STROKE);
mPaint.setStrokeJoin(Paint.Join.ROUND);
mPaint.setStrokeCap(Paint.Cap.ROUND);
// mBitmapPaint = new Paint(Paint.DITHER_FLAG);
// mBitmapPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC));
setColor(color);
setSize(size);
setState(state);
}
@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
}
@Override
protected void onDraw(Canvas canvas) {
// canvas.drawColor(Color.TRANSPARENT);
// canvas.drawBitmap(mBitmap, 0, 0, mBitmapPaint);
//
// if (state == 0)
// mBitmap.eraseColor(Color.TRANSPARENT);
for (int i = 0; i < paths.size(); i++) {
mPaint.setColor(colors.get(i));
mPaint.setStrokeWidth(sizes.get(i));
canvas.drawPath(paths.get(i), mPaint);
}
mPaint.setColor(color);
mPaint.setStrokeWidth(size);
canvas.drawPath(mPath, mPaint);
}
public void setColor(int color) {
this.color = color;
}
public void setSize(int size) {
this.size = size;
}
public void setState(int state) {
this.state = state;
// if (state == 0)
// mPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.CLEAR));
// else
// mPaint.setXfermode(null);
}
public void onClickUndo() {
if (paths.size() > 0) {
undonePaths.add(paths.remove(paths.size() - 1));
sizes.remove(sizes.size() - 1);
colors.remove(colors.size() - 1);
invalidate();
}
}
private void touch_start(float x, float y) {
undonePaths.clear();
mPath.reset();
mPath.moveTo(x, y);
mX = x;
mY = y;
}
private void touch_move(float x, float y) {
float dx = Math.abs(x - mX);
float dy = Math.abs(y - mY);
if (dx >= TOUCH_TOLERANCE || dy >= TOUCH_TOLERANCE) {
mPath.quadTo(mX, mY, (x + mX) / 2, (y + mY) / 2);
mX = x;
mY = y;
}
}
private void touch_up() {
mPath.lineTo(mX, mY);
mCanvas.drawPath(mPath, mPaint);
colors.add(color);
sizes.add(size);
paths.add(mPath);
mPath = new Path();
}
@Override
public boolean onTouchEvent(MotionEvent event) {
float x = event.getX();
float y = event.getY();
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
touch_start(x, y);
invalidate();
break;
case MotionEvent.ACTION_MOVE:
touch_move(x, y);
invalidate();
break;
case MotionEvent.ACTION_UP:
touch_up();
invalidate();
break;
}
return true;
}
}
Hello,
I create that thread to present you a tutorial aiming to learn you how to create a Running Man Game Animation on Android. You can discover the tutorial in video on Youtube :
Create a Running Man Game Animation on Android
Making a Running Man Animation on Android is a great way to learn how to work with Bitmaps, Thread and SurfaceView. First thing to make a Running Man Game Animation is to have a character to animate. For that, we will use the following sprite sheet :
{
"lightbox_close": "Close",
"lightbox_next": "Next",
"lightbox_previous": "Previous",
"lightbox_error": "The requested content cannot be loaded. Please try again later.",
"lightbox_start_slideshow": "Start slideshow",
"lightbox_stop_slideshow": "Stop slideshow",
"lightbox_full_screen": "Full screen",
"lightbox_thumbnails": "Thumbnails",
"lightbox_download": "Download",
"lightbox_share": "Share",
"lightbox_zoom": "Zoom",
"lightbox_new_window": "New window",
"lightbox_toggle_sidebar": "Toggle sidebar"
}
Like you can see, our character sprite sheet has 8 frames. Each frame show the character in a different position when he runs.
For our animation, we are going to create a custom GameView extending the SurfaceView class and implementing the Runnable interface. First, we need to define some properties like the game thread, the surface holder, the canvas where running man will be drawn, the bitmap used to load the sprite sheet and some parameters to customize the running man animation like the speed, the size or the position of the man on the screen :
Code:
class GameView extends SurfaceView implements Runnable {
private Thread gameThread;
private SurfaceHolder ourHolder;
private volatile boolean playing;
private Canvas canvas;
private Bitmap bitmapRunningMan;
private boolean isMoving;
private float runSpeedPerSecond = 500;
private float manXPos = 10, manYPos = 10;
private int frameWidth = 230, frameHeight = 274;
private int frameCount = 8;
private int currentFrame = 0;
private long fps;
private long timeThisFrame;
private long lastFrameChangeTime = 0;
private int frameLengthInMillisecond = 50;
// ...
}
To draw correctly the good frame for the running man, we need two Rectangle instances. One used to define the current frame in the sprite sheet and an other to define where to draw the current frame on the screen :
Code:
private Rect frameToDraw = new Rect(0, 0, frameWidth, frameHeight);
private RectF whereToDraw = new RectF(manXPos, manYPos,
manXPos + frameWidth, frameHeight);
On the GameView constructor, we get the surface holder and then, we load the sprite sheet into the bitmapRunningMan variable. We apply a scale transformation according values defined in frameWidth and frameHeight parameters :
Code:
public GameView(Context context) {
super(context);
ourHolder = getHolder();
bitmapRunningMan = BitmapFactory.decodeResource(getResources(),
R.drawable.running_man);
bitmapRunningMan = Bitmap.createScaledBitmap(bitmapRunningMan,
frameWidth * frameCount, frameHeight, false);
}
Now, it's time to make the event loop for animation inside the run method overrided from Runnable interface :
Code:
@Override
public void run() {
while (playing) {
long startFrameTime = System.currentTimeMillis();
update();
draw();
timeThisFrame = System.currentTimeMillis() - startFrameTime;
if (timeThisFrame >= 1) {
fps = 1000 / timeThisFrame;
}
}
}
Note that we animate the character while the playing variable is set to true. Like usual in a game, we update the elements and then we draw before to calculate frame per seconds. The update method is just used here to move the man positions in X and Y. Note that when the man reach the end of the screen horizontally or vertically, we set its position to the left or top of the screen :
Code:
public void update() {
if (isMoving) {
manXPos = manXPos + runSpeedPerSecond / fps;
if (manXPos > getWidth()) {
manYPos += (int) frameHeight;
manXPos = 10;
}
if (manYPos + frameHeight > getHeight()) {
manYPos = 10;
}
}
}
Before to write the draw method, we need to define a method to manage the current frame to display for the character. We change the current frame only when he have ended the frame duration :
Code:
public void manageCurrentFrame() {
long time = System.currentTimeMillis();
if (isMoving) {
if (time > lastFrameChangeTime + frameLengthInMillisecond) {
lastFrameChangeTime = time;
currentFrame++;
if (currentFrame >= frameCount) {
currentFrame = 0;
}
}
}
frameToDraw.left = currentFrame * frameWidth;
frameToDraw.right = frameToDraw.left + frameWidth;
}
And now, we define the draw method :
Code:
public void draw() {
if (ourHolder.getSurface().isValid()) {
canvas = ourHolder.lockCanvas();
canvas.drawColor(Color.WHITE);
whereToDraw.set((int) manXPos, (int) manYPos, (int) manXPos
+ frameWidth, (int) manYPos + frameHeight);
manageCurrentFrame();
canvas.drawBitmap(bitmapRunningMan, frameToDraw, whereToDraw, null);
ourHolder.unlockCanvasAndPost(canvas);
}
}
First, we check if the surface is valid. Then we lock the canvas and we draw the character current frame. Last, we unlock the canvas and post it on the Surface View. Finally, we define two methods to pause or resume the running man animation :
Code:
public void pause() {
playing = false;
try {
gameThread.join();
} catch(InterruptedException e) {
Log.e("ERR", "Joining Thread");
}
}
public void resume() {
playing = true;
gameThread = new Thread(this);
gameThread.start();
}
To start the running man animation, we're going to wait the user click on the surface view. So, we need to override the onTouchEvent method and wait for an ACTION_DOWN event. When the event is made, we have just to change the isMoving boolean value. If man is running, we stop it. If man doesn't run, we start to move it :
Code:
@Override
public boolean onTouchEvent(MotionEvent event) {
switch (event.getAction() & MotionEvent.ACTION_MASK) {
case MotionEvent.ACTION_DOWN :
isMoving = !isMoving;
break;
}
return true;
}
Last thing to make is to assemble all the pieces of the puzzle, create the game view on the main activity, set it as the content view and then resume or pause the game animation when the activity is resumed or paused :
Code:
package com.ssaurel.runningman;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Rect;
import android.graphics.RectF;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.util.Log;
import android.view.MotionEvent;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
public class RunningManAnimation extends AppCompatActivity {
private GameView gameView;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
gameView = new GameView(this);
setContentView(gameView);
}
@Override
protected void onResume() {
super.onResume();
gameView.resume();
}
@Override
protected void onPause() {
super.onPause();
gameView.pause();
}
class GameView extends SurfaceView implements Runnable {
private Thread gameThread;
private SurfaceHolder ourHolder;
private volatile boolean playing;
private Canvas canvas;
private Bitmap bitmapRunningMan;
private boolean isMoving;
private float runSpeedPerSecond = 500;
private float manXPos = 10, manYPos = 10;
private int frameWidth = 230, frameHeight = 274;
private int frameCount = 8;
private int currentFrame = 0;
private long fps;
private long timeThisFrame;
private long lastFrameChangeTime = 0;
private int frameLengthInMillisecond = 50;
private Rect frameToDraw = new Rect(0, 0, frameWidth, frameHeight);
private RectF whereToDraw = new RectF(manXPos, manYPos, manXPos + frameWidth, frameHeight);
public GameView(Context context) {
super(context);
ourHolder = getHolder();
bitmapRunningMan = BitmapFactory.decodeResource(getResources(), R.drawable.running_man);
bitmapRunningMan = Bitmap.createScaledBitmap(bitmapRunningMan, frameWidth * frameCount, frameHeight, false);
}
@Override
public void run() {
while (playing) {
long startFrameTime = System.currentTimeMillis();
update();
draw();
timeThisFrame = System.currentTimeMillis() - startFrameTime;
if (timeThisFrame >= 1) {
fps = 1000 / timeThisFrame;
}
}
}
public void update() {
if (isMoving) {
manXPos = manXPos + runSpeedPerSecond / fps;
if (manXPos > getWidth()) {
manYPos += (int) frameHeight;
manXPos = 10;
}
if (manYPos + frameHeight > getHeight()) {
manYPos = 10;
}
}
}
public void manageCurrentFrame() {
long time = System.currentTimeMillis();
if (isMoving) {
if (time > lastFrameChangeTime + frameLengthInMillisecond) {
lastFrameChangeTime = time;
currentFrame++;
if (currentFrame >= frameCount) {
currentFrame = 0;
}
}
}
frameToDraw.left = currentFrame * frameWidth;
frameToDraw.right = frameToDraw.left + frameWidth;
}
public void draw() {
if (ourHolder.getSurface().isValid()) {
canvas = ourHolder.lockCanvas();
canvas.drawColor(Color.WHITE);
whereToDraw.set((int) manXPos, (int) manYPos, (int) manXPos + frameWidth, (int) manYPos + frameHeight);
manageCurrentFrame();
canvas.drawBitmap(bitmapRunningMan, frameToDraw, whereToDraw, null);
ourHolder.unlockCanvasAndPost(canvas);
}
}
public void pause() {
playing = false;
try {
gameThread.join();
} catch(InterruptedException e) {
Log.e("ERR", "Joining Thread");
}
}
public void resume() {
playing = true;
gameThread = new Thread(this);
gameThread.start();
}
@Override
public boolean onTouchEvent(MotionEvent event) {
switch (event.getAction() & MotionEvent.ACTION_MASK) {
case MotionEvent.ACTION_DOWN :
isMoving = !isMoving;
break;
}
return true;
}
}
}
Now, you have just to run the application on your Android emulator or on your real device and to enjoy your Running Man Game Animation.
Don't hesitate to try this code and give me your feedbacks.
Thanks.
Sylvain
I just started learning self taught Android development and hopefully, I get to this level one days. Thanks for sharing your steps for this
Sent from my Nexus 6P using Tapatalk
nighthawk626 said:
I just started learning self taught Android development and hopefully, I get to this level one days. Thanks for sharing your steps for this
Sent from my Nexus 6P using Tapatalk
Click to expand...
Click to collapse
Great . I plan to make some tutorials for beginners too. Do you have some suggestions for that kind of tutorials ?
No suggestions, I'm just looking for a good place to begin. I first started with Android development then I scaled back to learn about Java, and still no luck, so I just started off with c++. Lol
Sent from my Nexus 6P using Tapatalk