I am making a simple game with JavaFX, where a ball bounces around the screen after being released from the bottom (via a button). When the ball is bouncing around the pane, if it hits a Rectangle it changes its color to blue.
I am attempting to add a method called checkBounds to keep track of when the ball (a Circle) hits the Rectangle. As soon as the ball comes into contact with the rectangle, the score should increase by 10. The scoring mechanism works, but it continues to increment with each frame that the ball takes through the Rectangle, instead of only incrementing once when it enters. (eg. It should only go up by 10 one time, not continue going up by 10 the whole time the ball passes through). I callcheckBounds() 3 times in my timeline loop to check each rectangle on each loop iteration.
checkBounds(ball, gameSquare);
checkBounds(ball, gameSquare2);
checkBounds(ball, gameSquare3);
How would I fix this logic error? I've attempted several different options, but none seem to work.
private void checkBounds(Shape block, Rectangle rect) {
boolean collisionDetected = false;
Shape intersect = Shape.intersect(block, rect);
if (intersect.getBoundsInLocal().getWidth() != -1) {
collisionDetected = true;
}
if (collisionDetected) {
score.setText(Integer.toString(currentScore.getCurrentValue()));
currentScore.incrementBy(10);
rect.setFill(Color.BLUE);
}
}
I believe what you want to detect is the state change of your collisionDetected from false to true. One way to do this is to keep the state of the previous value as a member variable for each collision object. To do this, we need some identifying id for the rectangle, so you may want to pass in the id into the checkBounds method:
private void checkBounds(Shape block, Rectangle rect, int id) {
we also need to create a member variable to store the states:
private HashMap<Integer,Boolean> previousCollisionStateMap = new HashMap<>();
and then inside your checkBounds code, you can modify the condition to check for changes
Boolean prevState = previousCollisionStateMap.get(id);
if (prevState == null) { // this is just to initialize value
prevState = false;
previousCollisionStateMap.put(id,false);
}
if (!prevState && collisionDetected) {
score.setText(Integer.toString(currentScore.getCurrentValue()));
currentScore.incrementBy(10);
rect.setFill(Color.BLUE);
}
and don't forget to update the state at the end
previousCollisionStateMap.put(id,collisionDetected);
Related
So, my problem is that I'm currently working on a path-finding technique in an open world with tiles of various sizes. The object needs to find an optimal path to a destination inside an infinite world (it generates on the fly), which is filled with tiles of various sizes (which are not located on a set grid - they can have any location and size - and neither have to be integers). (The object has access to the data of all the tiles via and ArrayList). Now some factors that make this problem more difficult:
The objects itself has a size and cannot move through tiles. Therefore, it is possible for a path to exist that is too narrow for the object to move through.
The target destination may itself be a moving object.
It is possible for there to be dozens of such objects at the same time - so it is necessary for the algorithm to either be light on the system or for the path to be calculated in a few separate ticks of the program.
I tried implementing solutions for maze-solving techniques, but the main problem is the in most mazes, the tiles can only have very specific coordinates (such as whole numbers) and are always the same size.
I also tried rendering the scene as a giant conventional maze where tiles are actually pixels of tiles (so if i have a 20x40 tile it becomes a 20x40 block of 1x1 tiles), but ran into performance issues and the still didn't solve the issue with a path potentially being to narrow for the object to fit through.
EDIT:
Terribly sorry for my poor wording before, that happens when I'm trying to rush to a solution without fully understanding the question. So what I'm using the algorithm for at the moment is for NPC enemies to find their way to the player around obstacles. Here is an example of a scene:
The black circle with an arrow is the player, the black bush-like blobs are the NPC enemies. So this my my current algorithm I'm using for the enemy AI:
void move() { //part of the Enemy class, this function is called once each tick for every enemy
PVector velocity = new PVector(speed*game.dt, 0); //speed is a pre-set float denoting the enemy's speed, game.dt is deltatim
velocity.rotate(atan2(game.player.location.y-location.y, game.player.location.x-location.x)); //game.player.location is a PVector of the player's position, location is a PVector of this enemy's position
boolean init_collision = getTileCollision(); //getTileCollision is a boolean of whether this enemy is colliding with any tile
location.add(velocity);
boolean collision = getTileCollision();
if (!init_collision && collision) { //if the enemy happens to spawn inside a tile, let is move out of it before checking for collision
location.sub(velocity);
if (desired_heading != -1) { //desired heading is the angle, in radians, of which 90-degree angle the enemy wants to move in, by default set to -1 (see my descrition of this algorithm below)
velocity = new PVector(speed*game.dt, 0);
velocity.rotate(desired_heading);
location.add(velocity);
if (getTileCollision()) {
location.sub(velocity);
velocity = new PVector(speed*game.dt, 0);
velocity.rotate(current_heading); //current heading the an angle, in radians, of which 90-degree angle the enemy is currently moving in. set to -1 by default but can not equal -1 if desired_heading is not -1
location.add(velocity);
if (getTileCollision()) {
location.sub(velocity);
desired_heading = -1;
current_heading = -1;
}
} else {
desired_heading = -1;
current_heading = -1;
}
} else {
float original_heading = velocity.heading();
desired_heading = radians(round(degrees(velocity.heading())/90.0)*90.0); //round to the nearest 90 degrees
velocity = new PVector(speed*game.dt, 0);
velocity.rotate(desired_heading);
location.add(velocity);
if (getTileCollision()) {
location.sub(velocity);
}
float turn = radians(90);
while (true) { //if it cant move, try rotating 90 degrees and moving
velocity.rotate(turn);
location.add(velocity);
if (!getTileCollision() && abs(round(degrees(current_heading)) - round(degrees(velocity.heading()))) != 180) {
current_heading = velocity.heading();
break;
} else {
location.sub(velocity);
}
}
}
} else {
desired_heading = -1;
current_heading = -1;
}
}
So what my terrible code hopes to accomplish is the the enemy first tries to move directly at the player. If it encounters an obstacle, it will round its angle to the nearest 90 degrees, set desired_heading to this and try to move through. If it cant, it will rotate another 90 degrees and so forth, always keeping the original rounded angle in mind.
This doesn't work remotely well as first of all, rotating 90 degrees has a 50% chance to go in the exact wrong diretion, so I tried adding
if (abs(original_heading - velocity.heading()+turn) < abs(original_heading - velocity.heading()-turn)) {
turn = radians(-90);
}
right before the while (true) but that broke the algorithm completely (sometimes the enemy will freeze in deep thought and not move ever again).
What am I doing terribly wrong? Should I try a different algorithm or does this one have potential?
I hope this is a better question now...
Here is a link to a video I recorded of my issue: https://sendvid.com/rjpi6vnw
You'll notice that initially, I start at tile (0, 0) then after moving my character up and down multiple times, the screen will only go up to (1,0). So I lost a whole row of playable map. I only lose part of the map when my screen adjusts itself. You'll understand what I mean in a moment. I have a class called Player, and in it I have methods called moveRight(), moveLeft(), moveUp(), and moveDown(). I'm excluding all useless classes and methods in order to not waste your time. Here are my moveDown() and moveUp() methods:
public void moveUp(){
locY1 -= defaultMoveAmount;
if(viewShouldMoveVertically(locX1, locY1) == true){ //locX1 and locY1 refers to the player's bounds location as set by setBounds()
Display.uni.moveMapDown(defaultMoveAmount); //Display.uni just means in the Display class
}
}
public void moveDown(){
locY1 += defaultMoveAmount;
if(viewShouldMoveVertically(locX1, locY1) == true){
Display.uni.moveMapUp(defaultMoveAmount); //defaultMoveAmount is the # of pixels the player moves each time the program updates
}
}
So I have KeyListeners that decide when these methods are called. The viewShouldMoveVertically() method is as follows:
public boolean viewShouldMoveVertically(int X1, int Y1){
if(Y1 < screenCenterY){ //screenCenterY is the number of vertical pixels on my screen/2
return false;
}
return true;
}
The moveMapUp() or moveMapDown() method is then called in the Display class:
int backgroundX1 = 0;
int backgroundY1 = 0;
public void moveMapUp(int moveAmt){
backgroundY1 -= moveAmt;
background.setBounds(backgroundX1, backgroundY1, backgroundX2 , backgroundY2);
}
public void moveMapDown(int moveAmt){
backgroundY1 += moveAmt;
background.setBounds(backgroundX1, backgroundY1, backgroundX2 , backgroundY2);
}
So if you can't view the video at the link I posted, I'll describe the issue. When my character moves close to the edge of the map, I obviously wouldn't want the camera to show areas off of the map. So the camera stops, but my character may continue walking up to the border of the map. If my character is within 540 pixels of the top of the map, the camera won't move(I'm running on a 1920x1080 display). This is intended. When I move the character more than 540 pixels from the top of the map, the camera will now move with the player since he's in the center of the screen. But the issue is that IF and ONLY IF the camera ends up moving away from the top of the map, then I now lose exactly "defaultMoveAmount" pixels from the viewable area when I return to the top again. I can't seem to figure out how to fix this issue. Now, a little more you may end up wanting to know: I have the same issue moving horizontally as I have moving vertically. It is set up in the same way, so there was no point in making you guys read extra code. When viewing the video at the link, I have to click on the play button at the bottom left, or else it tries to make me add an extension to Chrome or something. The solution to my program's issue may end up being quite simple, but I just can't seem to figure it out. I ended up getting sick of it and decided getting a little help would be better than giving up for now. I am a beginner to programming, as I've only had 1 year of programming experience from an AP Compute Science class. I'm sure you may see a few things that seem dumb, and I welcome any suggestions or comments you may have, but please be aware that I am fairly new to this stuff. Primarily motion. And finally, I did not post a compile-able section of code due to things such as the graphics that are required. While I'm on here, if you have any suggestions or good references for figuring out whether a character is within an area in a large tile-like map, such as a door that can be opened, it would be appreciated.
I have solved the issue. Here is my updated viewShouldMoveVertically() method:
public boolean viewShouldMoveVertically(int X1, int Y1){
if( Y1 <= screenCenterY && previousY1 > screenCenterY ){ //if just touched highest camera point from below
return true;
}else if( Y1 >= mapPixelTotalY-screenCenterY && previousY1 < mapPixelTotalY-screenCenterY ){ //if just touched lowest camera point from above
return true;
} else if( Y1 <= screenCenterY){ //if just touched highest camera point from above or is just above
return false;
} else if( Y1 >= mapPixelTotalY-screenCenterY ){ //If touched lowest camera point from below or is just below
return false;
}
return true;
}
It turns out that when the player touches the "highest camera point from below" or the "highest camera point from above", different results are required. If touching the highest camera view from below, the camera still needs to move upwards just one more time. But when touching that same point from above, the camera shouldn't move, since the player was coming from an area that the camera wouldn't be vertically moving at.You can see my solution in the code. Also, I added a "previousY1" that records what the last Y1 value was, so that I can tell whether the player came from above or below.
I'm having some difficulty implementing very basic collision within libGDX. The update code for the "player" is as so:
private void updatePosition(float _dt)
{
Vector2 _oldPos = _pos;
_pos.add(_mov.scl(_dt*50));
_bounds.setPosition(_pos.x-8, _pos.y-12);
if(_game.getMap().checkCollision(_bounds))
{
System.out.println("Collision Detected");
_pos = _oldPos;
_bounds.setPosition(_pos.x-8, _pos.y-12);
}
}
So, initially _oldPos is stored as the values of _pos position vector before applying any movement.
Movement is then performed by adding the movement vector _mov (multiplied by the delta-time * 50) to the position vector, the player's bounding rectange _bounds is then updated to this new position.
After this, the player's new bounding Rectangle is checked for intersections against every tile in the game's "map", if an intersection is detected, the player cannot move in that direction, so their position is set to the previous position _oldPos and the bounding rectangle's position is also set to the previous position.
Unfortunately, this doesn't work, the player is able to travel straight through tiles, as seen in this image:
So what is happening here? Am I correct in thinking this code should resolve the detected collision?
What is strange, is that replacing
_pos = _oldPos;
with (making the same move just made in reverse)
_pos.sub(_mov.scl(_dt*50));
Yields very different results, where the player still can travel through solid blocks, but encounters resistance.
This is very confusing, as the following statement should be true:
_oldPos == _pos.sub(_mov.scl(_dt*50));
A better solution for collision detection would be to have a Vector2 velocity, and every frame add velocity to position. You can have a method that tests if Up arrow key is pressed, add 1 (or whatever speed you would like) to velocity. And if down is pressed, subtract 1 (or whatever speed). Then you can have 4 collision rectangles on player, 1 on top of player, bottom, left, and right. You can say
if(top collides with bounds){
if(velocity.y > 0){
set velocity.y = 0.
}
}
And do the same for down, left and right (eg... for bottom, make sure to test if(velocity.y < 0) instead of if(velocity.y > 0).
EDIT: You code is not working because you set oldPos = pos without instantiating a new Vector2. Which means when you add onto pos, it also changes oldPos. So say oldPos = new Vector2(pos);
try to test future position before move. If collision, don't move.
My assignment asks me to code an ellipse similar to a plate, and stack 20 of them using if else statements and everything included from chapters 1-6 in shiffmans processing beginner's book. I need to use a button pressed function to stack 20 plates, once they reach to 20 THE "PLATES" MUST INDIVIDUALLY DISAPPEAR TO 0 ONE BY ONE WITH A MOUSE CLICK FUNCTION. This is what I have came up with so far, the plates have to start at the bottom of the screen.
// Declare global (shared) variables here
float plate1X = 50;
float plate1Y = 200;
int plateColor = (255);
// Do not write any statements here (must be inside methods)
void setup()`enter code here`
{
// Add statements to run once when program starts here. For example:
size(400,400);
plate1X = 200;
plate1Y = 50;
background(255);
plate1X = width/2;
plate1Y = height/2;
} // end of setup method
void draw()
{
// Declare local variables here (new each time through)
// Add statements to run each time screen is updated here
ellipse(plate1X, plate1Y, 200,50);
if(ellipse <= 1 || ellipse <= 0; //draw another plate);
// Screen will be repainted automatically at the end of draw method
} // end of draw method
// Add other methods here
"does not detect the variable 'ellipse'": That's because you didn't declare it. You need to let the compiler know what kind of variable it is before you use it. In this case, it is probably an integer, so you should have the line int ellipse; above the first time you use it.
You also never set ellipse to hold any value! You are trying to check if(ellipse <= 1 || ellipse <= 0), but right now ellipse doesn't have any value.
Your ellipse function seems like it should draw an ellipse at the coordinates that you tell it--
ellipse(x, y, width, height)
so if you want to draw another ellipse, you should call ellipse(...) again with a new x and y value.
You will want to use if statements to make sure you only do this some of the time--specifically when you haven't gotten to 20 plates yet. Do you know what if and else statements do?
I am using DigitalOnScreenControl to Move Player Sprite.But the Problem is that My Player Sprite goes out of Emulator Screen.I want player sprite be restricted to move in particular bounds and also camera to focus on player sprite as and when my sprite moves on.
i am trying this code:
public Scene onLoadScene() {
// Auto-generated method stub
this.mEngine.registerUpdateHandler(new FPSLogger());
final Scene mScene=new Scene();
mScene.setBackground(new ColorBackground(0.09804f, 0.6274f, 0.8784f));
final int centerX=(CAMERA_WIDTH-this.mPlayerTextureRegion.getWidth())/2;
final int centerY=(CAMERA_HEIGHT-this.mPlayerTextureRegion.getHeight())/2;
final Sprite player=new Sprite(centerX, centerY, this.mPlayerTextureRegion);
this.mCamera.setChaseEntity(player);
final PhysicsHandler physicsHandler=new PhysicsHandler(player);
player.registerUpdateHandler(physicsHandler);
mScene.attachChild(player);
this.mDigitalOnScreenControl=new DigitalOnScreenControl(0,CAMERA_HEIGHT-mOnScreenControlBaseTextureRegion.getHeight(),this.mCamera,this.mOnScreenControlBaseTextureRegion,this.mOnScreenControlKnobTextureRegion,0.1f,new IOnScreenControlListener() {
#Override
public void onControlChange(BaseOnScreenControl pBaseOnScreenControl,
float pValueX, float pValueY) {
// TODO Auto-generated method stub
physicsHandler.setVelocity(pValueX*100, pValueY*100);
}
});
this.mDigitalOnScreenControl.getControlBase().setBlendFunction(GL10.GL_SRC_ALPHA, GL10.GL_ONE_MINUS_SRC_ALPHA);
this.mDigitalOnScreenControl.getControlBase().setAlpha(0.5f);
this.mDigitalOnScreenControl.getControlBase().setScaleCenter(0, 128);
this.mDigitalOnScreenControl.getControlBase().setScale(1.25f);
this.mDigitalOnScreenControl.getControlKnob().setScale(1.25f);
this.mDigitalOnScreenControl.refreshControlKnobPosition();
mScene.setChildScene(this.mDigitalOnScreenControl);
return mScene;
}
Honestly I am Very new in andEngine Development
please Help me With Heart
Thank in advance.
Binding the Player to the Camera
What you're asking for is a form of collision handling. Essentially, when you've moved the player outside of the scene, you want to move the player back into the scene, and you want to do that in the same update as you've moved the player, immediately after moving the player, so the out-of-scene location never gets rendered.
Lets take apart what I just said. You move the player here:
physicsHandler.setVelocity(pValueX*100, pValueY*100);
Moving the player with a physicsHandler is perfectly reasonable and takes into account variable frame-rate so, so far, good job. Now you want to move them back into the scene if they're outside it. So we're going to need a new method. Lets throw in a method call right after moving the player (we'll call it "adjustToSceneBinding()" so you have this:
physicsHandler.setVelocity(pValueX*100, pValueY*100);
adjustToSceneBinding(player);
Sidenote, I'd rather have this method be on the player (i.e., player.adjustToSceneBinding()), but your player object is just a sprite. I'd suggest you change that to a custom object that extends sprite so you can throw in your own code there. In any event, lets make the adjustToSceneBinding() method.
public boolean adjustToSceneBinding(Sprite player) {
// Correct the X Boundaries.
if (player.getX() < 0) {
player.setX(0);
} else if (player.getX() + player.getWidth() > CAMERA_WIDTH) {
player.setX(CAMERA_WIDTH - player.getWidth());
}
}
See what I did? If the player's X coordinate is less than zero, move them back to zero. If the player's X PLUS the player's width is more than the camera width, move them back to the camera width minus the player width. Don't forget the player X coordinate represents the left side of the sprite. That's why I'm taking into account the width in addition to the position for calculating the right side.
The above code only addresses left/right movement, but I'm going to let you extrapolate how to do the same for up down.
Binding the Player to the Map Size Instead
Now, what we did so far was really intended for the situation where the map "size" is no larger than the screen. But often what you'll want is a larger map than the screen size and a camera that chases the player around. To do that, just replace the code above, where I referred to the CAMERA_WIDTH with a number that represents the width of your "map". In other words, the map can be much larger than the camera. If you use larger numbers there (than, say, the width of the camera), and you set the camera to chase the player entity (as you have), then you'll have a map where the player can move around until they hit the bounds of the map.
Hope this helps! :)