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.
Related
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));
}
I have a tester program which is used to build the start of a game.
I am having issues with the velocity attributes in the if statements which are supposed to create the bounce effect off the sprite of the screen but I can't seem to get the correct combination. Been working on this for a good while and cant seem to get any progress. Any help will be appreciated.
On a side note, in the else if methods there is a attribute called getWidth and getHeight, this is supposed to get the height and width of the screen. But I am unsure if it does. I can attach that class if needed. But below I will add the method I have for trying to create this "Bounce" effect of the edges of my screen.
public void checkScreenEdge(Sprite s){
if (s.getX() > getWidth()){
}
else if (s.getX() + s.getWidth() >= getWidth());
{
}
if (s.getY() > getHeight()) {
}
else if (s.getY() + s.getHeight() >= getHeight())
{
}
}
If you're using a deltaX and deltaY value (or velocity value) to decide which direction to move, simply change the value from positive to negative or visa versa at the appropriate location. For example:
if (s.getX() <= 0) {
s.setDeltaX(Math.abs(s.getDeltaX());
} else if (s.getX() + s.getWidth() >= MAX) {
s.setDeltaX(-Math.abs(s.getDeltaX());
}
I feel that it's important to use absolute value rather than directly swapping deltaX values, because if you did this:
if (s.getX() <= 0) {
s.setDeltaX(-s.getDeltaX());
}
you risk the sprite being "trapped" at the edges with the deltas being flipped repeatedly due to an over-shoot.
I'm developing a 2D video game using libgdx. I ran into a problem when I try to make jumping a body.
It does not jump as expected after making it moving to the right.(I can only move to the right or Jump)
If the body jumps before it's moving to the right everything goes fine. But If I decide to make jumping the body after moving it to the right. The body no longer jumps to the same height (It jumps less high). And I don't figure out why..
My method to jump the body :
if (player.isPlayerOnGround()) {
body.applyForceToCenter(0, 200, true);
}
My method to move the body right
if (player.isPlayerOnGround()) {
body.setLinearDamping(0f);
body.setLinearVelocity(1f,0f);
isMoving = true;
}
My method to stop the body moving right :
body.setLinearDamping(5f);
isMoving = false;
The world use a -9.81f gravity and the body 1f for the Mass.
P.S : Sorry for me bad english, it's not my native language.
Thank you.
First thing: never use forces to jump. Force has different effects based on how long that force acted. Second: don't use linearDamping. It makes your physics floaty and not real. You could use impulse instead of force in jumping method (it doesn't work very well actually). I'm using this method and it works perfectly
public void jump() {
if (jumpDelta >= Constants.PLAYER_JUMP_RATE) {
grounded = level.getContactListener().numFootContacts > 0;
if (grounded) {
body.setLinearVelocity(body.getLinearVelocity().x, 7);
jumpDelta = 0;
}
}
}
Where if (jumpDelta >= Constants.PLAYER_JUMP_RATE) prevents from too fast jumping (like two or more jumps at once), grounded = level.getContactListener().numFootContacts > 0; checks if player is on platform and finally this body.setLinearVelocity(body.getLinearVelocity().x, 7); changes body's vertical velocity. Changing velocity works better than applying impulse because impulse doesn't set velocity, it increases velocity. So if player was moving down with vertical velocity -3 m/s then its velocity will become 4, not 7 as we wanted.
P.S. Instead of linear damping i use this method
public void stopMoving() {
if (grounded) {
if (Math.abs(body.getLinearVelocity().x) <= 0.5f)
body.setLinearVelocity(0, body.getLinearVelocity().y);
else
body.applyLinearImpulse(-direction * 0.5f, 0,
body.getPosition().x, body.getPosition().y, true);
} else if (Math.abs(body.getLinearVelocity().x) <= 0.1f)
body.setLinearVelocity(0, body.getLinearVelocity().y);
else
body.applyLinearImpulse(-direction * 0.1f, 0, body.getPosition().x,
body.getPosition().y, true);
}
This method can seem too complex but it's really simple. First part handles body's movement on ground and second in the air. if-statements prevent from changing body's direction while stopping and direction variable can be 1 if body's moving right, -1 if body's moving left and 0 if body isn't moving.
I am working with images only and the dimensions of the window that I am using to view my application may be different on different systems. I have a mouse action listener that is listening for clicks on the main view of my program. I have a rounded rectangle that looks like a button. I want to make it so that way the mouse action listener only listens to the area of the rounded rectangle rather than the entire image on all systems. Like the title says, not the entire image has content, in particular, the corners don't look like they are part of the image, so I don't want to allow the user to be able to click on parts of the image without content and get the same result as if they clicked on the part with content.
My image looks similar to this
(source: youthedesigner.com)
So I only want the program to do something if the user clicks on the button inside the image rather than the nice stuff around the button.
This is what I have right now to listen to clicks:
#Override
public void mouseClicked(MouseEvent e) {
for(int i = 0; i <= 200; i++) {
if(e.getY() >= 100+i && e.getY() <= 300) {
if(e.getX() >= 10+100-Math.pow(10000-(Math.pow((i-100),2.0)),.5)) && e.getX() <= 10+400-Math.pow(10000-(Math.pow((i-100),2.0)),.5))) {
// do stuff
i = 201;
}
}
}
}
The math equation I am using in my code looks like 110-(10000-(y-100)^2)^(1/2)), which, if graphed, would look like an open parenthesis, and 410+(10000-(y-100)^2)^(1/2)), which would look like a close parenthesis 400 units away from the first graph.
The code works fine on my system, but on other systems, it doesn't work at all and I am curious how I could move the location I am listening to to correspond to how the image is scaled.
Thank you very much for any help you can provide.
The for-loop is superfluous.
You could ensure that pixels outside the button (.png) have some transparency, and then check for the alpha color component.
In this case you could add a Rect and look for that:
private boolean insideButton(Rectangle buttonRect, Point mousePt) {
if (buttonRect.contains(mousePt)) {
int r = buttonRect.height() / 2;
if (mousePt.x < r) {
// Left circle with O at (r, r)
int xFromO = r - mousePt.x;
int yFromO = r - mousePt.y;
if (xFromO * xFromO + yFromO * yFromO > r * r) {
return false; // Outside circle
}
}
if (mousePt.x > buttonRect.right - r) {
// Right circle:
...
}
return true;
}
return false;
}
So, I used Joop's answer to solve my problem. His answer wasn't quite what I was looking for, but it gave me the idea I needed to solve my problem. The solution I came to was:
private boolean insideButton(Rectangle buttonRect, Point mousePt) {
if (buttonRect.contains(mousePt)) {
int r = (int)buttonRect.getHeight()/2; // radius of either of the circles that make up the sides of the rectangle
if(mousePt.x <= buttonRect.getWidth()/2) { // if it is on the left of the button
Point center = new Point((int)buttonRect.getX()+r, (int)buttonRect.getY()+r); // the center of the circle on the left
double lengthToPoint = Math.pow(Math.pow(mousePt.x-center.x, 2)+Math.pow(mousePt.y-center.y, 2), 1.0/2); // length from center to the point that the user clicked at
if(lengthToPoint > r && mousePt.x < center.x) { // if it is to the left of the center and out of the circle
return false;
} else {
return true;
}
} else { // if it is on the right, the rest of the code is just about the same as the left circle
Point center = new Point((int)buttonRect.getWidth()-r, (int)buttonRect.getY()+r);
double lengthToPoint = Math.pow(Math.pow(mousePt.x-center.x, 2)+Math.pow(mousePt.y-center.y, 2), 1.0/2);
if(lengthToPoint > r && mousePt.x > center.x) {
return false;
} else {
return true;
}
}
} else {
return false;
}
}
I know it is goes a little overboard with calculations and inefficient, but I wanted to present it this way to show a better idea of how my solution works.
I can think of at least two ways.
The first is to produce a mask image (black and white), where (for example) white would indicate the clickable area. Basically, you could compare the pixel color of the mask based in click pick point of the original image.
The other way would be to build a image map, basically using something like a Shape API to allow for non-rectangular shapes. This would allow to use Shape#contains to determine if the mouse clicked inside it or not
In either case, you need to take into account the x/y position of the original image
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.