I have this update method shown below:
#Override
public void update(Input input, int delta) {
/* if UP, move player 5 tiles upwards */
if (input.isKeyPressed(Input.KEY_UP) {
y -= 5;
setY(y);
}
/* if DOWN, move player 5 tiles downwards */
if (input.isKeyPressed(Input.KEY_DOWN) {
y += 5;
setY(y);
}
/* if LEFT, move player 5 tiles to the left */
if (input.isKeyPressed(Input.KEY_LEFT) {
x -= 5;
setX(x);
}
/* if RIGHT, move player 5 tiles to the right */
if (input.isKeyPressed(Input.KEY_RIGHT) {
x += 5;
setX(x);
}
}
My update loop from my World class:
public void update(Input input, int delta)
throws SlickException {
// Update every sprite eg. Player, Blocks etc.
for (Sprite sprite : list) {
sprite.update(input, delta);
}
}
Where setX() and setY() are just setters from my class, which handle how many pixels the player should move in terms of tiles. Where each tile is 32 pixels.
So far this moves my player from one location to another location 5 tiles down, up, left or right. I was wondering if their was a way to make the player move one tile every 0.25 seconds to its destination? As in, every 0.25 seconds, the player would move 32 pixels in the left, right, down or up direction. I want this to be added so it looks like the player is sliding across tiles instead of teleporting straight to its location.
How could I use a timer to achieve this? Could I use delta to do this? Any sort of help would be appreciated.
See this answer:
Java slick2d moving an object every x seconds
You should definitly use delta for this since you want framerate independent motion in your game.
Keep a variable outside of the update loop that contains a timestamp.
int lastTimestamp;
Now, in your loop, make a conditional that checks if 250 milliseconds has passed since the time in lastTimetamp.
// Use System.nanoTime() / 1000000L to get current time
if(currentTime >= (lastTimestamp + 250)) {
movePlayer();
lastTimestamp = currentTime;
}
Now this condition should pass every 0.25 seconds.
I'd add a Runnable class MoveAnimation that you give to a
ScheduledExecutorService.
class MoveAnimation { // the full player movement
public boolean animationFinished();
public void animate() {
if (animationFinished()) {
throw new Exception(); // there is probably a cleaner way to remove this from the executor
}
}
}
class AnimationControl {
private final ScheduledExecutorService animationSchedule = Executors.newScheduledThreadPool(1);
public void addMovement(MoveAnimation animation) {
animationSchedule.scheduleAtFixedRate(animation::animate, 0, 250, TimeUnit.MILLISECONDS);
}
}
// in the event listener
if (input.keyIsPressed(Input.KEY_LEFT)) {
animationControl.addMovement(new MoveLeftAnimation(from, to));
}
Related
So, for a coding assignment we have to make a tank game. I created a bullet class using this:
package com.MyName.battletanks;
import com.badlogic.gdx.graphics.g2d.Sprite;
public class Bullet {
public Sprite b;
public float vx = 0;
public float vy = 0;
public Bullet(Sprite temp) { b = temp;}
public void move() { b.translate(vx*3,vy*3);}
}
My variables are as follows:
Sprite Player1;
Sprite Player2;
ArrayList<Bullet> bullets;
Upon Clicking space it creates the bullet using this:
if (Gdx.input.isKeyPressed(Input.Keys.SPACE)) {
Bullet bullet = new Bullet(new Sprite(new Texture("Bullet1.png")));
bullet.b.setPosition(Player1.getX() + Player1.getWidth() / 2, Player1.getY() + Player1.getHeight() / 2);
float rotation = (float) Math.toRadians(Player1.getRotation());
bullet.vx = (float) Math.cos(rotation);
bullet.vy = (float) Math.sin(rotation);
bullets.add(bullet);
}
Now, here is my code for getting my tanks to pass through one side of the screen to another:
if (Player1.getX() > Gdx.graphics.getWidth()){
Player1.setX(-64f);
} else if(Player1.getX()<-64f){
Player1.setX(Gdx.graphics.getWidth());
}
if (Player1.getY() > Gdx.graphics.getHeight()){
Player1.setY(-64);
} else if(Player1.getY() < -64f){
Player1.setY(Gdx.graphics.getHeight());
}
Now, Player 1 is a sprite, however, the bullets are created using an arraylist and a self made bullet class. As a result, I cannot use the code for Player1 that I did for the bullet. SO, my question is, how can I get my bullet to pass to the other side of the screen?
You can use the modulo % operator to do something like this:
bullet.position.x %= Gdx.graphics.getWidth();
bullet.position.y %= Gdx.graphics.getHeight();
This isn't tested but it should work. Also, I noticed you're using 2 floats for your velocity, and you should really be using Vector2 because then you can easily scale and normalise it, which would be useful for a velocity.
It is really the same thing for the Player class and for the Bullet class since you have the same screen-space therefore you can reuse your current code by making it into a function that takes any sprite. This function would be defined as follows:
void fitOnScreen(Sprite sprite) {
if (sprite.getX() > Gdx.graphics.getWidth()){
sprite.setX(-64f);
} else if(sprite.getX()<-64f){
sprite.setX(Gdx.graphics.getWidth());
}
if (sprite.getY() > Gdx.graphics.getHeight()){
sprite.setY(-64);
} else if(sprite.getY() < -64f){
sprite.setY(Gdx.graphics.getHeight());
}
}
You would call this function on the Player as well as loop over every bullet, such as:
fitOnScreen(Player1);
for (Bullet b: bullets) fitOnScreen(b.b);
Well, what you are asking is how can you run through each instance of the bullets and change their properties.
I have to agree #Zac's algorithm would work fine for one bullet, but you need to put it in a loop to go through each and every bullet in order to be effective.
Here is an example of what you could do in your render() method of your game screen:
Iterator<Bullet> it = bullets.iterator();
while(it.hasNext()) {
Bullet bullet = it.next();
// Now you do the position correction
bullet.b.setPosition(bullet.b.getX()%Gdx.graphics.getWidth(), bullet.b.getY()%Gdx.graphics.getHeight());
}
Of course there are other ways of doing this, but this is probably the easiest way. You could also recycle the code you used for you player in this loop too.
Also, note that with this method, the more bullets, the more laggy the application becomes.
I have just started learning about AndEngine, and was following a tutorial on making the Sprite move, thought i'd figure out if i wanted to reverse it after it went out of the screen,i'd do this.
#Override
public void move() {
this.mPhysicsHandler.setVelocityX(-100);
this.mPhysicsHandler.setVelocityY(100);
OutOfScreenX();
OutOfScreenY();
}
// ===========================================================
// Methods
// ===========================================================
private void OutOfScreenX() {
if (mX > MainActivity.CAMERA_WIDTH) { // OutOfScreenX (right)
mX = 0;
} else if (mX < 0) { // OutOfScreenX (left)
mX = MainActivity.CAMERA_WIDTH;
}
}
private void OutOfScreenY() {
if (mY > MainActivity.CAMERA_HEIGHT) { // OutOfScreenX (right)
this.mPhysicsHandler.setVelocityY(-1000);
Log.d("Changing Direction","Changed");
} else if (mY < 0) { // OutOfScreenX (left)
mY = MainActivity.CAMERA_HEIGHT;
}
}
Unfortunately, all this does is making the Sprite bounce at the bottom of the screen. Is there any reason to fix this and how? I understand that another way of doing this is to reverse the Velocity by multiplying it by negative one, but that doesn't seem to work either.
Would appreciate if someone can point me in the right direction!
Thanks!
I assume you are calling the move()-method steadily.
When you do so, the y-velocity is set to 100, then the OutOfScreenY-method is called to check the position of the sprite. In this method the y-velocity is set to -1000, so the sprite is moving the opposite direction.
But the move()-methos is called again, setting the velocity back to 100. Then again, the OutOfScreenY-methos is called an changes the velocity to -1000.
The velocity is changing steadily between 100 and -1000 what would explain the bouncing.
I' m currently writing a simple 2D game in java from scratch (for learning purposes)
I want to control the rate at which a player can shoot. The method done there works, but it could be improved. The method is getting called if the user presses/holds the left mouse button. It works when the user is holding the button pressed, but when he/she releases the mouse button, waits (more then the rateOfFire time) and tries to shoot it may or may not work, because the roftC value isn' t getting updated when the player doesn' t shoot.
I tried then to put it into my update() method (which gets called 60 times a second). The problem still exists. I really have no idea how to solve this. Here is my code:
/**
* Used to control the rate of fire
*/
private int roftC = 0;
/**
* Shoot a Projectile
*/
protected void shoot(int x, int y, double dir) {
Projectile p = new Bomb(x, y, dir);
if (roftC % p.getRateOfFire() == 0) {
level.addProjectile(p);
}
if (roftC > 6000) {
roftC = 0;
}
roftC++; // Whether it is here or down there doesn' t make a diffrence
}
/**
*
*/
#Override
public void update() {
// roftC++;
}
One idea would be to introduce a minimum delay between shots. Something like this:
static final long MINIMUM_DELAY = 1000/30; // So we can have 30 shots per second
long lastShotTimestamp;
protected void shoot(int x, int y, double dir) {
long now = System.currentTimeMillis();
if (now - lastShotTimestamp > MINIMUM_DELAY) {
level.addProjectile(new Bomb(x, y, dir));
lastShotTimestamp = now;
}
}
Such an approach is actually close to the physics - a gun needs some time to reload between consecutive shots.
I'm trying to make balls fall from the top of the window. I store ball objects in an ArrayList and, at the moment, I am doing this.
for (int i = 0; i < balls.size(); i++) {
Ball b = (Ball) balls.get(i);
if (b.isVisible()) {
b.move();
}
the move function just changes the y co-ordinate of the ball so it drops down the screen.
At the moment, it is all being painted at exactly the same time and fall at exactly the same time.
e.g. http://puu.sh/xsGF
How do I make it so they fall at random intervals?
My move() function is as follows.
public void move() {
if (y > 480) {
this.setVisible(false);
System.out.println("GONE");
}
y += 1;
}
You could add balls randomly during the game loop.
//add new balls randomly here:
if(<randomtest>) {
balls.add(new Ball());
}
for (int i = 0; i < balls.size(); i++) {
Ball b = (Ball) balls.get(i);
if (b.isVisible()) {
b.move();
}
else {
//also might be good idea to tidy any invisible balls here
//if you do this make sure you reverse the for loop
}
}
There are 2 things you can do:
Add a Timer. When the Timer goes off (every 10 ms for example), select a random ball, and let that one drop 1px. (Mind, you will get balls that will fall at different speeds at different times, because of the random factor)
Use a random value for the speed when initializing the ball. Increase the y coordinate by that speed value, so the balls will all fall at a different rate through the sceen.
The simplest approach, if you want constant velocity, is to place them im random positions putside the top of your viewport.
Since I guess you already draw them outside the screen just add a random displacement there and you are done. eg:
ball.y = -radius + random.nextInt(100);
Ok, seeing your move function, this is not really physically correct. You should have a acceleration. This makes the ball fall more realistically (of course there is air resistance etc, but I think this is enough for now). In order the let them fall at random times, you could either add them at random times (make them existing/visible at random time instances) or so.
class Ball {
private double acc = 9.81; // or some other constant, depending on the framerate
private double velocity = 0;
private double startFallTime = Math.random()*100; // set from outside, not here!
public void move() {
// check if ball is already here
if (startFallTime-- > 0) return;
if (y > 480) {
this.setVisible(false);
System.out.println("GONE");
}
velocity += acc;
y += velocity;
}
}
EDIT: Of course the acceleration stuff is optional, depending on what you want. If you want linear movement, then your approach is fine, it just looks better if the ball has an acceleration. ;) Also, I recommend adding the balls at random instances and not work with this startFallTime that I used, because this is physically not really correct. Depends on your needs though, so you have to figure out the right way by yourself.
I'm trying to show the famous mouth opening/closing animation of the pacman character in a throwaway pacman game I'm making to teach myself game programming.
What I'm doing is drawing the open mouth image, then redrawing the closed mouth image at the exact same (x/y) location. But this doesn't work, and I just see the closed mouth animation all the time.
If I put this in a loop, the system just freezes and you see flickering where the open mouth image this, but you don't see the images being replaced.
I've tested and made sure that both images are being loaded correctly and as expected.
Here's my startAnim() function, its called when you double click at the applet:
public void beginGame() //Called from engine.java
{
isRunning=true;
repaint();
pacman.startAnim();
}
public void startAnim() //In different class, pacman.java
{
Image orig;
while (engine.isRunning)
{
orig=this.getCurrentImg();
draw(engine.getGraphics());
this.setCurrImg(currImg2);
this.draw(engine.getGraphics());
this.setCurrImg(orig);
this.draw(engine.getGraphics());
try
{
Thread.sleep(100);
}
catch (InterruptedException e) {}
}
}
public void draw(Graphics g) //Called from engine.paint()
{
g.drawImage(getCurrentImg(), getX(),
getY(), engine);
}
you have to sleep between the 2 images. otherwise you will only see the last image painted.
eg.
while( running )
{
image 1
draw
sleep
image 2
draw
sleep
}
something like this:
public void startAnim() //In different class, pacman.java
{
final int cnt = 2;
Image[] imgs = new Image[ cnt ];
int step = 0;
imgs[ 0 ] = closedMouthImage;
imgs[ 1 ] = openMouthImage;
while ( engine.isRunning )
{
this.setCurrImg( imgs[ step ] );
draw(engine.getGraphics());
step = ( step + 1 ) % cnt;
try
{
Thread.sleep(100);
}
catch (InterruptedException e) {}
}
}
As sfossen said, you need a delay between drawing the images.
A couple of other things to consider.
For smooth animation, you probably need more than just the "mouth open" and "mouth closed" images. You need two or three intermediate images.
To make resource management easier, you might want to put all of your animation frames together in a single, wide image, that will look like a "filmstrip". Then, to draw a frame, you use the (x, y) offset for the frame your interested in.
Finally, if you draw all the frames each time you go through the main loop of your program, Pac-Man will do a complete chomp every time you move him. You should think about drawing just one frame each time through the main loop and using a variable to track which frame you're on.
Example (pseudocode)
frameWidth = 32
frameIndex = 0
while(running) {
// Draw just the frame of your animation that you want
drawImage(pacmanX, pacmanY, filmStrip, frameIndex * frameWidth, 0, frameWidth, frameHeight)
frameIndex = (frameIndex + 1) % frameCount
// Update position of pacman & ghosts
// Update sound effects, score indicators, etc.
}