So I've made a basic collision system in love2D using basic OOP.
function love.update(dt)
player:update(dt)
for _, rect in pairs(randomRectangles) do
local collides,mtv,side = collider:collidesMTV(player,rect)
if collides then
print(side)
player:collide(collides,mtv,side)
end
end
end
mtv being the minimum translation to move the part when it collides and the side being the side it collides on.
The problem is, I want to be able to make it so in the player:collide function, shown here:
function player:collide(colliding,mtv,side)
if colliding==true and side=="top" or colliding==true and side == "bottom" then
self.Position=self.Position-mtv
end
if colliding==true and side=="left" or colliding==true and side == "right" then
self.Position=self.Position-mtv
self.Gravity=-0.1
end
if not colliding and self.Gravity ~= -0.95 then
self.Gravity=-0.95
end
end
I can make it so when it's NOT colliding, it sets the gravity back to normal, but if I add an elseif/else statement inside for when it's not colliding, it'll also do that if it's colliding with one block since there's 30 other blocks on the screen and won't set gravity to -0.1 since it'll be always setting it back to normal gravity, if that makes any sense.
How can I fix that?
Maybe simply extract the collision behavior outside of the collide method? Here's an idea:
function player:collide(colliding,mtv,side)
if colliding==true and side=="top" or colliding==true and side == "bottom" then
self.Position=self.Position-mtv
end
if colliding==true and side=="left" or colliding==true and side == "right" then
self.Position=self.Position-mtv
self.hasSideCollision = true
end
end
function player:computeGravity()
if player.hasSideCollision then
self.Gravity = -0.1
else
self.Gravity = -0.95
end
end
function love.update(dt)
player:update(dt)
player.hasSideCollision = false
for _, rect in pairs(randomRectangles) do
local collides,mtv,side = collider:collidesMTV(player,rect)
if collides then
print(side)
player:collide(collides,mtv,side)
end
end
player:computeGravity()
end
Dealing with gravity should be dealt with at the player level, not the individual collision's.
EDIT
Took direction into account and moved gravity logic into its own method.
Related
I'm currently making a game and I made this simple script that checks when a player holds down the up arrow key so that the longer you hold the button, the further upwards you jump. It works fine and all, until you decide to jump again mid-air, then the gravity of the is_action_just_pressed is added into the player's total gravity which is kind of a problem.
I'm kind of new to GDScript and programming in general, so a little help would really be appreciated!
Here's my (bad) code:
var gravity : int = 1800
var vel : Vector2 = Vector2()
func _physics_process(delta):
vel.x = 0
vel = move_and_slide(vel, Vector2.UP)
vel.y += gravity * delta
if Input.is_action_just_pressed("jump") and is_on_floor():
vel.y -= jumpforce
gravity -= 500
if Input.is_action_just_released("jump"):
gravity += 500
There isn't anything in godot for checking if the player is holding down a button and I'm not sure what is wrong with my code, so I have no idea what is causing this issue or how to fix it.
There isn't anything in godot for checking if the player is holding down a button
Instead of using is_action_just_pressed and is_action_just_released which tell you if the action was pressed or released since the last frame, use is_action_pressed which tell you if the action is currently pressed.
It works fine and all, until you decide to jump again mid-air, then the gravity of the is_action_just_pressed is added into the player's total gravity which is kind of a problem.
The issue is that you are not keeping track if player jumped. For example:
if Input.is_action_just_pressed("jump") and is_on_floor():
jumped = true
vel.y -= jumpforce
gravity -= 500
if Input.is_action_just_released("jump") and jumped:
jumped = false
gravity += 500
With jumped declared at the top of the script, similar to gravity and vel.
First time posting here. Tried to look for topics previously to help.
I'm using Visual Basic, but so far I've been able to follow C# and just translate into VB.
I would like collision without tiles. Smooth movement without any sort of snapping. I already have the movement down, and my sprites stop at the edges of the screen.
I've read I could use Bounds and Intersects, which I have tried. When I apply an IF statement to the arrow keys each time they are pressed, using Bounds and Intersects (I just prevent sprite movement if it is intersecting), it works for ONE key. I move left into an object, and I stop. If I apply the IF to all keys, it will work the first time. Say I move left into an object, the IF statement checks if the Intersects is true or not and acts accordingly.
Now I want to move right, away from the object. I can't since my sprite is ALREADY colliding with the object, since each arrow key is programmed to NOT move if there is Intersection. I see perfectly why this happens.
The code I currently have: (Each arrow key has the same code, altered to it)
If Keyboard.GetState(PlayerIndex.One).IsKeyDown(Keys.Right) And rBlockBounds.X <=
graphics.GraphicsDevice.Viewport.Width - rBlockBounds.Width = True Then
If rBlockBoundBoxBounds.Intersects(rObstructBounds) Then
rBlockBounds.X += 0
rBlockBoundBoxBounds.X = rBlockBounds.X - 1
Else
rBlockBounds.X += 1
rBlockBoundBoxBounds.X = rBlockBounds.X - 1
End If
End If
rBlockBounds is my sprite As Rectangle
rBlockBoundBoxBounds is another Rectangle (1 pixle bigger than rBlockBounds) used as a Hit Box more or less that moves with rBlockBounds, and is the thing doing the collision checking
rObstructBounds is the stationary object that I'm moving my Sprite into.
Anyone have suggestions on how I can make this work?
Since I myself program in C#, not VB I can not code your solution but instead I can explain a better way of approaching it.
What you want to do is prevent the two rectangles from ever intersecting. To do this you will need to implement a move method into your code which can check if the two tiles are colliding. Here is a C# example:
public bool MoveX(float distance) // Move Player Horizontally in this example
{
rBlockBounds.X += distance;
if(rBlockBoundBoxBounds.Intersects(rObstructBounds)
{
rBlockBounds.X -= distance;
return false;
}
return true;
}
Which essentially means that if you run into an object you will be pushed out of it. Since it occurs in one tick you won't get any jutty back-and-front animations.
And that should do what you want. You can test this out and then implement it for y-coordinates as well.
Also, you might notice I've made the function return a bool. This is optional but allows you to check if your player has moved or not.
Note if you teleport an object into another one it will cause problems so remember to implement this every time you move anything.
But that should do what you want.
Edit
Note since your objects are not in a tiled grid, you will need to move lots of time in very small steps.
Something like this:
public bool MoveX(float distance) // Move Player Horizontally in this example
{
rBlockBounds.X += distance;
if(rBlockBoundBoxBounds.Intersects(rObstructBounds)
{
rBlockBounds.X -= distance;
return false;
}
return true;
}
public bool MoveX(float distance, int repeat)
{
for(int i=0; i < repeat; i++)
{
rBlockBounds.X += distance;
if(rBlockBoundBoxBounds.Intersects(rObstructBounds)
{
rBlockBounds.X -= distance;
return false;
}
}
return true;
}
Where the second one will take multiple steps. Here is why you would use it:
MoveX(500); // Will move 500 right. Could easily skip over objects!
MoveX(5, 100); // Will move 5 right one hundred times
// ^ This will take more time but will not skip object
Similarly for yours you could do this:
MoveX(3); // If contact object will be max 3 pixels away
MoveX(1, 3); // If contact object will be max 1 pixels away
MoveX(0.5f, 6); // If contact object will be max 0.5 pixels away
Now I am guessing all your x, y positions are integers. If so you could get away doing the second call and come exactly next to each other. If not you would do the third call.
Hope this helped.
I'm playing with libGdx, creating a simple platformer game. I'm using Tiled to create the map and the LibGdx tiledMap renderer.
It's a similar setup to the SuperKoalio libgdx example.
My Collision detection at the moment, it just determining whether the player has hit a tile to the right of it, above it or below it. When it detects a collision to the right, it sets the players state to standing.
Control of the player is done through the InputHandler. When the D key is pressed, it sets the players state to walking, when the key is released, it sets the state to standing.
My problem is, that if I'm holding down D, and I jump and hit a platform and stop, even when the player has dropped back down and should be able to continue moving, it won't, not until I release the D key and press it again. I can jump fine, but not walk.
Any ideas on why this is and how I can fix it? I've been staring at it for so long that I might be missing something obvious, in which case a fresh pair of eyes might help.
This is the code I've got right at the start of my player.update function to get the player moving.
if(state == State.Standing) {
velocity.x = 0;
} else if(state == State.Walking || state == State.Jumping) {
velocity.x = MAX_VELOCITY;
}
And this is an extract of the collision code :
for (Rectangle tile : tiles) {
if (playerRect.overlaps(tile)) {
state = State.Standing;
break;
}
}
Originally, the collision response set x velocity to 0, and the velocity was used to determine the state, which still produced the same problem.
Thanks
As your Collision-detection is allready working, the thing you need to change is the collision handling.
You set the Players state to Standing.
Instead of doing this you culd set a flag collision and in the update check this flag:
if(state == State.Standing || collision) {
velocity.x = 0;
} else if(state == State.Walking || state == State.Jumping) {
velocity.x = MAX_VELOCITY;
}
This way you know, if you don't move becuase you can't (collision==true) or if you don't move, because you don't press the key (state != State.Standing)
Of course you also need to know, when you don't collide anymore.
For this you could reset the collision flag after setting the velocity and recalculate it the next frame.
this is more of an advise thread I guess.
I've been wondering how one could create a button which display "play" when it's not pressed. And then shows "pause" once it's pressed. And visa versa when it's pressed again.
I had a similar problem when trying to create an expand panel button, but that was easy because I could just set a variable to true or false if PanelCollapsed was true.
But in this case I couldn't find any property in a button that I could query.
So I came up with this but I can't help thinking that this is a rather unsmart way of doing it?
If isPlay = True Then
If isPaused = False Then
btnPlay.Image = Image.FromFile("iconPause.png")
isPaused = True
isPlay = False
End If
GoTo Endline
End If
If isPlay = False Then
If isPaused = True Then
btnPlay.Image = Image.FromFile("iconPlay.png")
isPaused = False
isPlay = True
End If
End If
Endline:
How about using only one variable and code like this:
If isPlay Then
btnPlay.Image = Image.FromFile("iconPause.png")
else
btnPlay.Image = Image.FromFile("iconPlay.png")
End If
isPlay = not isPlay
You can use the "Tag" property. Its type is "object" so you can use any object you want, but in your case a string will do:
If Button1.Tag = "Pause" Then
Button1.Image = Image.FromFile("iconPlay.png")
Button1.Tag = "Play"
Else
Button1.Image = Image.FromFile("iconPause.png")
Button1.Tag = "Pause"
End If
Most .NET WinForm controls have a 'Tag' property (a button has one). You can set the Tag to be anything you want. An easy way to do this is to set the 'Tag' property to a boolean with the state of the button.
Just an idea...sure there are many other approaches.
UPDATE:
Otherwise, you can maintain the state of the button in your application as its own member variable. This might have several advantages because you can pass this state to other controls that might need it. The only weakness with this approach is that the state must be maintained separately.
If you have a fairly straight-forward implementation, use the Tag property.
A contrary opinion ...
... while other answers have given you some techniques to achieve your desired result, I'm going to ask you to reconsider your UI design.
Dual state buttons - ones that alternate purpose when clicked - can be a source of user frustation.
Here are two scenarios.
Scenario #1 ... if the users machine is under load (for any reason), there may be a perceptible delay between the users actual click on your button and when your click handler is executed.
Normally the time between click and handler is a few milliseconds or less, but it can run to several seconds. If this happens when the user clicks on a dual state button, they are likely to click the button again. Net effect, when the application catches up, is to toggle on, then immediately off again.
Scenario #2 ... many users habitually double click everything. Even experienced users who've been using computers for years may have this weird habit. When they try to press a dual state button, guess what happens ... the action toggles on, then immediately off again.
There are at least two solutions ...
Solution #1 ... use two buttons, one for "On", one for "Off".
Solution #2 ... write some debouncing code to suppress the effect of a second click if processed immediately (ie: < 75ms) after the first.
I don't personally use Visual Basic, but I do know that Buttons in Windows Forms have a property called 'Tag'. It is of the generic object type, so you can save whatever state you want, and just use casting to get the value back out.
How about using the "Image" property?
Rem form initialization
ImagePlay = Image.FromFile("iconPlay.png")
ImagePause = Image.FromFile("iconPause.png")
Button1.Image = ImagePlay
.
.
.
Rem on button1 click
If Button1.Image = ImagePlay Then
Button1.Image = ImagePause
Else
Button1.Image = ImagePlay
End If
In my glut application I'm simulating a plane with the camera. When the planes speed is low I intend to have the nose start to point towards the ground as the camera falls. My first instinct was to just change the pitch until it was pointed downwards at -90degrees. However I can't just change the pitch because if the plane is tilted on its side or upside down then it would note be changing direction towards the ground.
Now i'm trying to do a rough simulation of this by shifting the 'lookAt.y' downwards. To do this I am trying to get all the current camera coordinates that I use to set the camera
(eye.x, eye.y, eye.z, look.x, look.y, look.z, up.x, up.y, up.z). Then recall the set with the new modified values.
I've been working with the Camera.cpp and Camera.h to control my camera functions. They can be found here
after adding methods to get all the values, only the eye values are actually updated when various camera motions are made. I guess my question is how do I retrieve these values.
The glLoadMaxtrix call is in this function
void Camera :: setModelViewMatrix(void)
{ // load model view matrix with existing camera values
float m[16];
Vector3 eVec(eye.x, eye.y, eye.z);
m[0] = u.x; m[4] = u.y; m[8] = u.z; m[12] = -eVec.dot(u);
m[1] = v.x; m[5] = v.y; m[9] = v.z; m[13] = -eVec.dot(v);
m[2] = n.x; m[6] = n.y; m[10] = n.z; m[14] = -eVec.dot(n);
m[3] = 0; m[7] = 0; m[11] = 0; m[15] = 1.0;
look.x = u.y; look.y = v.y; look.z = n.y;
glMatrixMode(GL_MODELVIEW);
glLoadMatrixf(m);
}
Is there a way to get 'eye', 'lookAt', and 'up' values from the matrix here? Or should I do something else to get these values?
-Thanks in advance for your help
The camera class you link to is not an actual OpenGL class, but it should be simple enough to work with.
The function quoted just takes the current values of the camera object and sends them to OpenGL. If you look at the camera's set function, you can see how the program calculates the values it actually stores.
The eye value is stored directly. The lookAt value is just the value of (eye - n), by vector math. The up value is the hardest, but if I remember my vector math correctly, I believe that up = (n cross u).