I am currently making a 2D Platformer in Java. I am a beginner so take it easy.
I have a problem with the gravity in the game. I am using different variables for falling and jumping. I am using a tiled map. So lets get to the point.
My fall method works like this -
if(collisionDown == false) {
characterY += fall;
fall ++;
{
fall is equal to 4. and If collisionDown is true it resets back to 4.
My jump method is almost the same:
if(key.E == true && collisionDown == true) {
characterY -= jump;
jump --;
}
jump is equal to 16. and If collisonDown is true it resets back to 16.
Now problem is: imagine the character is jumping. its in the air and while going down characterY += fall; lets say characterY = 250 and fall is equal - 15 at that exact moment. The next solid tile below the character starts at Y position 255. character is at 250 and does NOT detect collision so next frame it adds 15 to characterY which is 250 + 15 = 265. At that point the character has "entered" the solid tile which was at positionY 255.
I have "fixed" that so the character gets back ON TOP of the solid tile (and that is visible and annoying.) That is not the perfect solution because it slows the character 1 frame each time it enters a solid tile(which is because it detects left and right collision and the character can't move). The character visibly stutters if I can say it like that.
I need a solution for that problem but can't think of any.
So if you make a suggestion I would be happy. Thank you.
I would probably use something like the following.
if(collisionDown == false) {
characterYnext = characterY + fall; //Get Next Position
if(NextMovementCollides()){ //Basically if next position is too far.
characterYnext += difference_between(CharacterY,Ground); //This should move the character to the ground state.
fall = 0; //No longer falling so reset the value.
}
else{characterY += fall; fall++;} //Otherwise continue falling like normal.
}
DISCLAIMER: I'm not a java programmer so my syntax might be a bit off.
This should work, just plug in your game logic where it would make sense.
The way I usually handle this, is to pre-check movement:
private int moveDown(int fall){
if (isSolidBlock(characterX, characterY + fall)){
//Knowing the height of your block, calculate some kind of reduction. If your block height is 40, it's probably something like (characterY + fall)%40
fall = 4;
collisionDown = true;
return maxFallUntilSolidBlockReached();
}
fall++;
return fall;
}
private boolean isSolidBlock(int x, int y){
//Implement some kind of check if at (x,y) there's a solid block.
}
Then just do this for the fall calculation:
if(collisionDown == false) {
characterY += moveDown(fall);
}
Related
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've finally completed the collision detection, however the collision response is very glitchy (if you try you can go through walls) and that's mainly because I have no information about the collision angle!
I have camPos(x,y,z) coordinates of my camera aswell as the min and max values of model(minX,minY,minZ,maxX,maxY,maxZ).. with a simple test I check if the camPos is interecting the model boundaries:
if(cameraX > xMax || cameraX < xMin) {
collisionX = false;
collisionY = false;
collisionZ = false;
} else {
collisionX = true;
System.out.println("collisionX: "+ collisionX); // collision on x is true!
}
I have acess to all vertice positions and calculated the min max values of the object to create a BoundingBox.
In order to get the right direction in which I want to push the object I need to know in which direction the nearest face is pointing (left,right,forward,back?)
To find out the angle I thought I could use the normal coordinates which I also have acess to, since they indicate a 90 degree angle to the face, right?
console print: //all 'vn' values of cube.obj
xNormals:0.0
yNormals:0.0
zNormals:0.0
xNormals:0.0
yNormals:0.0
zNormals:0.0
xNormals:1.0
yNormals:1.0
zNormals:1.0
xNormals:0.0
yNormals:0.0
zNormals:0.0
xNormals:-1.0
yNormals:-1.0
zNormals:-1.0
xNormals:0.0
yNormals:0.0
zNormals:0.0
Basically I want to know how these normal coordinates have to be applied to the min and max values of the object so that I can define all faces of the BoundingBox for example: face A is defined by xMin_a and xMax_a and faces left, face B is defined by xMin_b and xMax_b and faces right and so on..
I hope it's a bit more understandable, it's quite hard to explain..
Okay I've figured out a way to do it, hope it will help someone else too: I found out how to calculate the faces of a bounding box, it was actually not even that hard and it had absolutely nothing to do with normals.. sorry for the confusion.
As shown in my answer you can calculate the boundingBox of objects with their min/max values.. but you can also define all six faces of the box by extending the check and use a boolean to find out if the respective face collides somewhere:
if(collisionX == true && collisionY == true) {
if(cameraZ+0.2 > zMin_list.get(posIndex)-0.2 && cameraZ+0.2 < zMin_list.get(posIndex)) {
faceA = true;
System.out.println("faceA = " +faceA + " face a is true!");
} else {
faceA = false;
}
}
You can do this for all six faces where A(Forward), B(Back), C(Left), D(Right), E(Up) and F(Down) will define where the collision direction should be:
If the collision is on Face A, the response will be moving backwards (on z axis minus).. That way you will never be able to glitch through a wall because it will always push you away from the collision area.
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.
This question is unlikely to help any future visitors; it is only relevant to a small geographic area, a specific moment in time, or an extraordinarily narrow situation that is not generally applicable to the worldwide audience of the internet. For help making this question more broadly applicable, visit the help center.
Closed 9 years ago.
I am trying recreate the board game "go" in Java. I am currently working on the capturing system. Basically once a stone has been surrounded by the enemy stone on all four sides (diagonal don't count) you remove that stone. Like in the screenshot below.
Or if multiple of the same stones are connected you have to surround all the open sides. Like in the screenshot below.
In both cases the black stones should be removed at this point. This link explains more on the rules of capturing stones. societies.cam. ac. uk /cugos/go/rules_03.html
I was told it would be best to use recursion to do this. After doing some research on recursion I managed to write some code. But it's not working. It only seems to detect the enemy stone on the second move of the game. I call my method every time a stone is placed in my mouseReleased.
public static boolean checkCapture(int x, int y)
{
{
if ((board[x][y + 1] != move) && (board[x][y + 1] != 0)) //bellow
{
System.out.println("enemy is bellow");
if (checkCapture(x, y + 1))
board[x][y] = 0;
} else if (board[x][y + 1] == 0)
{
return false;
}
if ((board[x][y - 1] != move) && (board[x][y - 1] != 0)) //above
{
System.out.println("enemy is above");
if (checkCapture(x, y - 1))
board[x][y] = 0;
} else if (board[x][y - 1] == 0)
{
return false;
}
if ((board[x + 1][y] != move) && (board[x + 1][y] != 0)) // right
{
System.out.println("enemy is right");
if (checkCapture(x + 1, y))
board[x][y] = 0;
} else if (board[x + 1][y] == 0)
{
return false;
}
if ((board[x - 1][y] != move) && (board[x - 1][y] != 0)) //left
{
System.out.println("enemy is left");
if (checkCapture(x - 1, y))
board[x][y] = 0;
} else if (board[x - 1][y] == 0)
{
return false;
}
}
return true;
}
My int x is my column and my int y is my row, move is my variable that holds whose turn it is( 1 = black , 2 = white) board is my 2d array that holds the position of all the stones on the board.
I think that recursion complicates this solution more than necessary. If I were to implement something like this, I would take the following steps:
Find connected groups of stones. You can limit this to just dragons if you can detect if a group is alive because it has two eyes.
For each group of connected stones, count the liberties vertically and horizontally. (Liberties are unoccupied locations adjacent to a connected group of stones.) If the number of liberties is 0, then the group is captured.
If you are checking for a capture after a move has been made, then you really only need to check the connected groups which are adjacent to the most recent move, not all connected groups.
First, start out by being explicit about what your function does.
/**
* Checks to see if the grid point passed in is captured.
* #param...(you should say what your params are here
**/
public static boolean checkCapture(int x, int y) {
//some code
}
This is important: what if this function checks to see if the gird point is capturing some other, arbitrary point? Further, we immediately see a problem... captured by who? Whenever solving a recursive problem you need to understand the base case: here it is that there is no vertical or horizontal adjacent area that isn't occupied by an enemy.
Therefore, we must check to see in regards to a particular color:
/**
* Checks to see if the grid point passed in is captured.
* #param...(you should say what your params are here
**/
public static boolean checkCapture(int x, int y) {
if (!isOccupied(x,y)) {//writing this function should be trivial
return false;//can't be captured; no one is there!
}
Color color = getColor(x,y);//similarly, get the color of whoever is there.
Status status = checkFlanked(x, y, color);
if (status = Status.FLANKED) {
return true;
}
}
private static Status checkFlanked(int x, int y, Color color) {
//check to see that this location is valid for the board
//check to see if this square is occupied at all
//if it is not, return LIBERTY (an empty space means no capture, right?)
//if it is, is it occupied by the opposite color? --> Return a FLANKED result!
//if it is, is it occupied by the same color? --> recurse!
}
Now we've broken down our problem a bit! And it's easy to see how the base case is resolved: if the square is unoccupied, it can't be flanking... so it returns a LIBERTY result. If it's occupied by the opposite color, then this square is flanking whomever you were originally checking. The only difficult part is then checking to see whether, in the case of this being occupied by the original color, any other locations have liberty or not.
//get all valid adjacent locations
//call checkFlanked on those locations.
//If any return LIBERTY, return LIBERTY. Otherwise return FLANKED.
(Note: I'm assuming LIBERTY and FLANKED have been defined as an enum for clarity's sake.)
I hope this helps you break down your problem in a more sensible way. Remember: when you're using recursion, you care about two cases: the base case, and the '+1 iteration' case. Note that even with the above you have to solve some problems:
You need to intelligently not recurse back to squares you've already visited. (Investigate tail recursion, but you can also just pass in additional state indicating squares that are checked already.)
You need to make sure you don't fall off the board and return an appropriate result if you do. Basically, you need to solve the 'what is a valid location?' problem.
Some other interesting questions to ask are:
Do you search by-breadth or by-depth?
Is this appropriate as a static method, or should it be captured in a class?
I have some code that lets you play and capture stones. See this answer: https://gamedev.stackexchange.com/questions/23291/go-game-placing-stones-on-grid-intersections/23406#23406
The trick is to keep track of contiguous blocks of stones and then check after every move to see if that move captures a block.
There is also ko to worry about.
The problem with recursion in this case is it is very easy to fall into an infinite loop. Checking two stones, checking the right stone will check the left stone will check the right again etc. You need to keep track of the stones you have already checked. You are going to need to pass some state of the stones that you have already checked.
The collection detection method I'm using currently can interpret a collision, but causes strange effects depending on the direction. It will:
Always work if the player is hitting the right side of the object.
Push the player to the side if the player is hitting the top or bottom of the object.
Work the first time if the player is hitting the left side of the object, but will teleport the player to the opposite side of the object the next time a collision is detected on the left side.
This is the current collision detection code:
if(player.playerBounds.intersects(portal.bounds)&&player.isMovingLeft){
player.playerX=(portal.x+portal.width);
player.playerX++;
}
else if(player.playerBounds.intersects(portal.bounds)&&player.isMovingRight){
player.playerX=(portal.x-player.width);
player.playerX--;
}
else if(player.playerBounds.intersects(portal.bounds)&&player.isMovingUp){
player.playerY=(portal.y+portal.height);
player.playerY--;
}
else if(player.playerBounds.intersects(portal.bounds)&&player.isMovingDown){
player.playerY=(portal.y+player.height);
player.playerY++;
}
Can you try the following code and see if it works?
bool collide = player.playerBounds.intersects(portal.bounds);
if(collide && player.isMovingLeft){
player.playerX = (portal.x + portal.width) + 1;
}
else if(collide && player.isMovingRight){
player.playerX = (portal.x - player.width) - 1;
}
else if(collide && player.isMovingUp){
player.playerY = (portal.y + portal.height) + 1;
}
else if(collide && player.isMovingDown){
player.playerY = (portal.y + player.height) - 1;
}
Possible reason could be:
checking for collision multiple times.
moving the player is changing the direction of movement.
some other code is conflicting with this code.
isMovingX is not working correctly.
Going to answer my own question here. The flag for direction moving in was never being changed from true, which was the root of the problems. One direction was normal because it lined up with the first direction I would usually move in.