Background: I've been doing a lot of studying in xna lately. I've been perfecting my game structure and creating essential classes for an average game, read a couple articles, watched a number of videos, and read bits of a book I have. So, after creating a good game structure I moved on to the actual game, a platformer. I currently have ordinary blocks, and a Player - and the problem is within the player.
Problem/Question: Colliding with blocks is usually fine, but sometimes collisions act strangely. IE: Some how the player is in the block ( normally one pixel in ), and this is a big problem because this could cause the player to be pushed some where across the map.
Player Game Logic: The player's movement is state based. The states are an enumerator of "ground, jump, and fall". Right now, the problem seems to be inside of the falling state. Sometimes the collision works correct, sometimes the player ends up one pixel inside of the block - which means if the player jumps he will be instantly pushed to the first block above the player - no matter how far.
Collision Logic: Inside the fall state the player is pushed towards the floor by gravity. Gravity gets added by the force every frame. Before changing the position, a collision method is called upon checking if there was a collision between any block within it's range of motion. Now, since this is a falling state the player is moving down, so when it collides a while loop pushes the player down by 1pixel every frame until it's meeting the block. And just as a back up, there's a loop that pushes the player out of the block. Then it sets the current state to ground. The jumping state follows the same kind of theory.
Falling State Script: Read above for explanation of this code.
case PlayerState.Fall:
// falling sprite
mySpriteManager.mySpriteCurrent = mySpriteManager.mySpriteIndex["PlayerSprites/player_fall"];
// fall velocity change
myVelocity.Y += myDforce;
if ( Collision( otObjectList.wallIndex, myPosition.X + 1, myPosition.Y, myPosition.X + myWidth - 1, myPosition.Y + myHeight + myVelocity.Y) )
{
while( !Collision( otObjectList.wallIndex, myPosition.X + 1, myPosition.Y, myPosition.X + myWidth - 1, myPosition.Y + myHeight) )
myPosition.Y += 1;
while ( Collision(otObjectList.wallIndex, myPosition.X + 1, myPosition.Y, myPosition.X + myWidth - 1, myPosition.Y + myHeight))
myPosition.Y -= 1;
myVelocity.Y = 0;
myCurrentState = PlayerState.Ground;
}
else
{
myPosition.Y += myVelocity.Y;
}
break;
Collision Script: This runs through all solid objects until there is a collision and returns true if there is.
private bool Collision( List<ObjWall> otWalls, float x1, float y1, float x2, float y2)
{
myBoundingBox.X = (int)Math.Ceiling(x1);
myBoundingBox.Y = (int)Math.Ceiling(y1);
myBoundingBox.Width = (int)Math.Ceiling(Math.Abs( x2 - x1));
myBoundingBox.Height = (int)Math.Ceiling(Math.Abs( y2 - y1));
foreach (ObjWall wall in otWalls)
{
if ( Box.Intersects( wall.Box ) )
{
return true;
}
}
return false;
}
Variable Explanations:
myBoundingBox - every normal object builds off this abstract class. So, every object has a bounding rectangle.
Box - is a trait that comes from the abstract object class, it just returns the bounding rectangle.
myVelocity - is the players velocity, this is a Vector2 because floats provide cleaner looking gravity.
myDforce - is a float that changes the yvelocity in a positive force. I believe I should make it a constant.
ObjectList - this is a class that runs all Normal Object logic and contains a list of all the possible objects that can be in a room.
myPosition - This is another protected variable provided by the abstract class that normal objects are driven from. This is a vector 2, so it works using Floats.
For any more explanation in the code please ask!
In advance, thanks for your time and Help.
Cay.
Your 2nd call to Collision currently only checks the current position and then moves the Player down regardless of whether that action would cause a collision.
I can see you should be dealing with this with your 3rd call to Collision and moving the player back up but to remove some complexity from your code (slightly), you could simply have the 2nd call to collision checking the position + 1 and not moving the player if true.
This removes the need for the 3rd call to Collision, matches the logic you're using for most of the falling with the first call to collision (i.e. moving by gravity but not if the move would collide with a wall) and reduces the places your error can occur.
Related
Hey stackoverflow Community,
First of all:
I'm still very new about programming with C# and Unity.
My question:
I'm working on an idea for a Movement of a Cube.
It is planned that the cube will move forward by pressing a key (W-Key). But it shouldn't just move forward. It should jump forward to the next point. So always plus 1 of its axis into which it should go. Accordingly, it is only intended to go forward, right, down, left. He won't be able to jump over behind. You should also see that the cube jumps in the respective direction, so it should not teleport itself. :D
Does anyone have an idea how I can realize this movement?
I am very much looking forward to your ideas.
(Sorry if my English is not so good, my English not the best. ^^)
best regards
xKarToSx
So, in order to understand movement, it's best to first understand Vectors in Unity. Since you want to be moving the cube in the forward direction, I'm going to assume this is a 3D game, in which case you want to use a Vector3.
A Vector3 has three components: X, Y, and Z. Each component is tied to an axis. In simple terms, X is tied to left and right, Y is tied to up and down, and Z is tied to forward and back. So, Vector3 position = new Vector3(0, 1, 2); will be a vector that is 1 unit above and 2 units in front of the starting position.
Assuming you've attached this script to the cube you want to move, you can track its position with transform.position. So, if you want to move the cube one unit forward, your code would look something like this:
if(Input.GetKeyDown(KeyCode.W)) // This code will activate once the user presses W.
{
transform.position += new Vector3(0, 0, 1);
}
That will move the cube one unit forward in the Z direction. However, you don't want it to teleport, you want to see it move, correct? In that case, you want to check out Unity's Vector3.Lerp function. Basically, you would use it to smoothly transition an object between two defined positions. You'll need to implement a timer and a for loop in order to make this work correctly.
So, to summarize, for moving one unit forward in the Z direction, your code would look something like this:
if(Input.GetKeyDown(KeyCode.Z))
{
float startTime = Time.time; //Time.time is the current in-game time when this line is called. You'll want to save this to a variable
float speed = 1.0f; //The speed if something you'll want to define. The higher the speed, the faster the cube will move.
Vector3 startPosition = transform.position; //Save the starting position to a different variable so you can reference it later
Vector3 endPosition = startPosition + Vector3.forward; //Vector3.Forward is equivalent to saying (0, 0, 1);
float length = Vector3.Distance(startPosition, endPosition); //You'll need to know the total distance that the cube will move.
while(transform.position != endPosition) //This loop while keep running until the cube reaches its endpoint
{
float distCovered = (Time.time - startTime) * speed; //subtracting the current time from your start time and multiplying by speed will tell us how far the cube's moved
float fraction = distCovered / length; //This will tell us how far along the cube is in relation to the start and end points.
transform.position = Vector3.Lerp(startPosition, endPosition, fraction); //This line will smoothly transition between the start and end points
}
}
I hope this helps you. This is my first time answering a question so sorry if I got some things wrong/it's not the most optimized. Good Luck!
For Context: I'm 90% of the way through making a wall running system in Unity, and I need help finding 2 Vector3's that I can use for a check and for adding velocity to the player to get them to move in the correct direction.
More specifically, I'm getting two Vector3's to the right and left of a contact point with a wall, then getting the distance between the right point and the players previous position, and the distance between the left point and the players previous position. Whichever point is further away from the player, is the direction we are travelling, and if you take that point and - the contact point from it and normalize that, the function returns the Vector3 to use to add velocity to the player.
Currently I'm using this function to do this for me (where hit is the contact point between the player and the wall):
Vector3 CalculateWallRunDirection(Vector3 hit, Transform wall)
{
Vector3 rightPos = hit + wall.forward;
Vector3 leftPos = hit + (wall.forward * -1);
float rightDis = Vector3.Distance(rightPos, prevPlayerPos);
float leftDis = Vector3.Distance(leftPos, prevPlayerPos);
if (rightDis < leftDis)
{
return Vector3.Normalize(leftPos - hit);
}
else
{
return Vector3.Normalize(rightPos - hit);
}
}
This works well when it works... but the problem is, because I'm using the Transform.forward of the wall to determine where these points are, if you try to wall run on the front or back face of an object, it will throw to into the wall instead of along it and the player will become stuck.
TL;DR: I need another way to determine two Vector3's instead of using transform.forward + original Vector3. From a top-down view, the problem could be displayed as this. A is the player, B is the contact point with the wall. Find C and D:
Please explain your answer, I'm not too great at math.
You were essentially trying to recreate Vector3.ProjectOnPlane(). You probably want to use that instead.
eg
Vector3 A = Vector3.ProjectOnPlane(B, C);
// A is the new velocity vector parallel to the wall
// B is the old velocity of the player at the point of impact
// C is the normal of the plane of impact
You can easily find C in the OnCollisionEnter event, simply get the contact point's normal.
Note B doesn't need to be normalized, but if it is, A will be a normalized Vector3 as well.
I don't know how can I do it. You lose a game, if gameobject falls down and collides with the collider. I want to add a function that asks to the player if he wants another try, if yes it will reverse some time so the gameobject that fall down will be back on the screen and the user is allowed to play once again. Is there some sort of time function/method to do this?
Hope you understand.
void OnCollisionEnter2D(Collision2D col) {
if(col.gameObject.tag=="Collider") {
Vector2 checkpoint = new Vector2(transform.position.x, transform.position.y +5)
}
}
void Reset(){
this.transform.position = checkpoint;
}
I created a child object for that collider. And I moved the child to where I wanted the player to live when I collided with the collider.
Void OnCollisionEnter2D(Collision2D col)
{
if(col.gameobject.tag=="die")
{
checkpoint = col.transform.GetChil(0).position;
}
}
void Reset(){
Player.transform.position = checkpoint;
}
Your error is because you are missing the new keyword before Vector2
When using C# you need to use the new keyword in front of all
constructors. Constructors are like functions that are used to create
new objects (or scructs). They always have the same name as the type
of object you are creating.
So you should write
Vector2 checkpoint = new Vector2(transform.position.x, transform.position.y +5)
instead of:
Vector2 checkpoint = Vector2(transform.position.x, transform.position.y +5)
Edit: Now you edited your question without the error, and since you are still trying to find a method that moves back in time what happened in Unity, I can tell you that that thing doesn't exist. However you can implement it yourself. I would recommend you something like this:
If it is a platform 2D game, like Mario Bros style, you can place in
your scene a sequence of trigger that are activated when the player
go through them.
Then you can have a boolean array with a length n equal to the
number of triggers, and then a matrix nx2 ( number of triggers x
coordinates of each trigger [x,y] ).
Initially all the elements in the array has a false value, and
every time a trigger is activated the correcponding element in the
array changes to true.
When the player dies, it checks all elements in the array, until it
reaches the last true element (this would be the last triggered
activated).
With the index form the previous step you access the matrix of
triggers and you extract both elements (x and y coordinate). Then you
use that value in:
Using your script:
Vector2 checkpoint = new Vector2(xCoordinate, yCoordinate);
There is no in built function to reverse time in unity, this would require unity to record all events up until that point.
But given your example, what you could do is create a 'checkpoint' where the scene is set up in a certain way and objects are in specific positions. Like in Mario where you fall down, it plays some music to show you failed then it puts Mario back in a certain position. Just have a function that changes the player position to the checkpoint position. The same applies to any other gameobject you need to reset.
public void Reset()
{
player.position = checkpoint.position;
}
I thought I'd be able to find this with some searching on the internet but everything I find is just balls bouncing off walls for something like pong or another arbitrary question. I'm making a 2D dungeon crawler game and when I kill enemies and they drop loot I want the item to come flying out as if it had just been thrown in the air and land a random point on the tile the unit was on.
I've been trying to figure this out myself but I can't figure it out, this is probably asked a lot, I'd be really grateful if someone could help me out.
EDIT AS REQUESTED:
Ok well when a monster would be destroyed I would choose a random location within the tile it's in, let's call this location endLoc and the monster's location startLoc. I would then find the center x point between these two locations and decrease the y by 20 ( because that's how many pixels i want the item to go up by), so let's called this variable launchLoc:
launchLoc = new Vector2(startLoc.X + ((endLoc.X - startLoc.X) / 2), startLoc.Y - 20)
I think that produces the right Vector.
So now I would need to launch the item from startLoc, to launchLoc, then have it come back down to endLoc. This is where it gets confusing and I'm not sure how to make a realistic arc for this. The end result would have the item move like it moved along a gaussian, as if it was thrown into the air.
I tried to make it so during each interval, the velocity is increased by 120th, of the X difference, between the startLoc and launchLoc, by an incrementing multiple, but I couldn't get it to work very well. I'm not sure if this was the best way to do. I use 120th because the y value is 20, and the item moves up 1 pixel every interval, so 1 to 20 added up gives 120, this would make the x movement constantly increase, like it was thrown up.
This is in 2D btw, I hope that helps.
You start with an initial velocity vector at time t0 (v(t0)) and position (p(t0)). Gravity can be assumed to produces a constant acceleration (a(t0) = <0, -9.8 m/s2>, though your value may differ) until the object lands. So the general form of the motion for going from one timeslice to the next is:
p(t) = 0.5*a(0)*(t-t0)2 + v(0)*(t-t0) + p(0)
v(t) = a(0)*(t-t0) + v(0)
To figure out when to stop that motion, you need to figure out at what time the object's path will intersect the surface against which it bounces. You'll have to do this for all of the surfaces for which this can reasonably be expected to happen. So for a plane with line equation Ux + Vy + T = 0 you break the position vector into its components, as in:
p(t) = <px(t), py(t)> Then use the quadratic formula to find tc where p(tc) satisfies the line equation:
0.5*(Uax(t0)+Vay(t0))*tc2 + (Uvx(t0)+Vvy(t0))*tc + (Upx(t0)+Vpy(t0)+T) = 0Chose the branch such that tc > t0. From there it's simple to figure out where the object will collide with the surface. You can then update the velocity vector and position vector based on the behavior of the bounce. If the plane is axially aligned (ie, it's a horizontal plane with normal vector parallel to the Z axis), then just flip the sign of the Z component of the velocity vector and multiply the whole velocity vector by some damping factor d, where 0≤d<1 to damp out the velocity. Then repeat until some predetermined time has passed or the velocity reaches some minimal amount (your call on that).
It becomes a bit more difficult with arbitrarily oriented planes. You will need to calculate the angle of incidence of the collision and reflect the velocity vector about the plane normal. I won't go into the details here, as I suspect you're probably not interested in it.
I'm pretty new to C# and XNA and well programing (like I can follow a tutorial but most of the creating it on my own is still really really hard). Right now I'm going round and round trying to figure out how to do this one "simple" thing.
Here's the idea, its going to be a tower defense game; right now I'm working on bare bone basics. I've got my little sprite guy who will move around with Keyboard input, now I want to click somewhere on the screen and have him "walk" to that point. Only I'm lost on the logic. I can click and he'll jump there with
if (aMouse.LeftButton == ButtonState.Pressed)
{
Position.X = aMouse.X;
Position.Y = aMouse.Y;
}
From what I've read for other mousing input, I'm thinking I'll need some kind of loop (bool maybe?) that will move the sprite in a direction and will have to run a check to see if he's got to that point yet. But getting that point after the mouse click and creating that loop, running the check...I'm clueless.
You need to add some instance variables:
Point2D targetPos;
And some constants:
const Point2D speed;
When you run through your Update() loop, update the current position by adding the speed vector to it (in the correct direction of course) until you are within a predefined threshold from the target position (usually the thresholds are calculated from the speed vector - if the distance from the current position to the target position is less than the length of the speed vector, then you're at your position). Using a bool in this case would work well. When you click your mouse set another instance variable (moving) to true, and once you've reached your target position, set moving to false.
People are answering your question at a very low level. Sometimes it help to think of the problem at a higher level.
What you need is a type of state management. The fancy Computer Science term for that is a Finite State Machine. But don't bother looking that up right now. It's fairly dry and confusing at first :)
Your character currently has one state - "standing around". You need to add and process the "walking to destination" state.
From what I've read for other mousing input, I'm thinking I'll need some kind of loop
You need one loop, called the game loop. If you're using XNA, you've already got one. But you're on the right track.
Every time through the game loop, you should process the current state, and check for what are called State Transitions. That's when you change something in your world from one state to another state. For example, when you click the mouse, you want the guy to start moving.
In your game loop you check to see if a mouse click just happened. If it did, then set up some data (where to move to), and tell him to start walking by setting his state to "walking to destination". Next update, you'll process that state instead.
When your character is in the "walking to desintation" state, you need to update their position, based on the amount of time that passed since the last game update. In XNA, this is calculated for you. If you're not using XNA, then you'll have to check yourself. You might be able to use something like the Stopwatch class, and check the Elapsed field.
If the character is at the destination, you need to switch them back to the "standing around" state.
If you receive another mouse click, it is up to you if you want the "walking to destination" state to pay attention to it or not. If you do pay attention to it, you set up the same sort of data as when you transitioned from the "standing around" state.
So, you'll need these variables:
A timer, to find out the elapsed time since the last game loop (XNA gives it to you)
The current player state (maybe an enum)
The current player position (a vector)
The walking speed of the player (a float, probably), measured in units per second (or millisecond)
Data for the "walking to destination" state - target position (another vector)
Data related to user input (mouse events that occurred since the last update, the position of those clicks, etc)
The character specific data will be different for each character in your game, so you want a new copy of it for each. You'll probably want to put it in a class. The rest of it is more global, so you can keep it separate, or make it part of your game, game loop, input classes, etc (however you choose to organize it).
I won't cover the vector math for how to actually calculate the partial movement stuff, since other people have covered that. No sense in duplicating those answers. It basically boils down to making a vector between your current position and the target position, and multiplying/dividing it by your walking speed (to chop it up to the distance moved in a single update).
I'm assuming you have three things:
Current position
Desired position
Speed to move each 'game tick' // don't know what a game tick is? find out!
You're looking at doing this
// dx, dy are delta x and delta y. It's how far in each direction
// the player must travel
// note, I fixed a typo where they were desired - desired... should be
// desired - current, as they are now
float dx = desiredX - currentX;
float dy = desiredY - currentY;
// d uses the pythagorean theorum to find the distance between current and desired
float d = sqrt(dx*dx + dy*dy);
// fac is how far along that line between desired and current will you move
float fac = d / speed;
// mx is the component of the dx line proportional to the size of fac : d
// which means it's how far in the x direction you'll move
float mx = dx * fac;
float my = dy * fac;
// the new postition is the old position plus the move value
float newPositionX = dx + mx;
float newPositionY = dy + my;
I have found this code to be most useful... additionally I have added an extra couple lines to prevent certain situations from occuring. For instance there will be times where direction is 0.83 and speed may have been modified by game factors like terrain/wheather/etc.... if speed is below 1, the sprite may not move at all or even move in the wrong direction!
if (Vector2.Distance(Position, TargetPosition) > 2.0f)
{
velocity = Vector2.Subtract(TargetPosition, Position);
velocity.Normalize();
/// Now no matter which direction we are going we are always moving # sprite.Speed
/// at velocity or speed below 1 - problems occur where the unit may not move at all!!
if(current_Speed < 1)
{
Vector2 temp = (velocity * 10) * (current_Speed * 10);
Position += temp / 10;
}
else
{
Vector2 temp = velocity * current_Speed;
Position += temp;
}
//convert to int to render sprite to pixel perfect..
Position = new Vector2((int)Position.X, (int)Position.Y);
}