Method to 'rock' a value back and forth - c#

I'm working on a small game, I have objects which I want to elevate up and down. Object moves to max value of Y -> Object moves to min value of Y -> Repeat. I have a rough idea of how to do this, I would put this in a timer/my update method.
if(Y >= max)
{
direction = "down";
}
if(y =< min)
{
direction = "up";
}
if(direction == "up") Y -= speed;
if(direction == "down") Y += speed;
(Could also use a bool ofcourse but, for the sake of implicity)
But it feels like I'm just re-inventing the wheel, is there by any chance a built in method/math function to do this automatically? eg. SomeFunction(min, max, increment).
I'm using the XNA framework, so functions built into that are ofcourse welcome as well.

Forget having a separate direction flag.
Just use a negative speed for "up" to simplify the code:
if ((Y >= max) || (Y <= min)) // Hit an edge?
speed = -speed; // Reverse direction.
Y += speed;

Related

MathHelper.WrapAngle() rotation issue

My game involves ships moving and rotating around a target (i.e., an enemy ship). Rotation depends on whether the user wants to rotate by port/starboard, or just by the closest side to the enemy.
The problem:
The angles are being wrapped with MathHelper.WrapAngle(). Keeping the angles between PI and -PI works great, until the rotating ship gets to the point where -3.141 becomes 3.141 (and vice versa). For example, the ship is rotating to port correctly, then when it hits this line it flips over starboard, then back to port again, then starboard again and so on!
I would be very grateful if the community could point out:
What I can do to make the ship rotate logic work correctly when going over the PI/-PI wrap 'barrier'
Point out any inefficiencies in my code (I'm sure there are many, and I'm sure there are many other ways to do this more efficiently)
Link to any relevant articles or tutorials that can help me overcome this issue (this is my first game)
Additional Information:
Ship.ShipMoveState.NoMoveRotate is essentially a flag that tells the ship to rotate (starting at 100th of max speed up until we hit max speed) either port or starboard, whichever is closest. The ship rotates to these sides as that is where the weapons are located. ShipMoveState.AwaitFurtherOrders is tells the ship to rotate depending on the difference in angle between closest side (port/starboard) and angle to enemy.
ShipCompartment primeCompartment = TargetShip.CenterCompartment;
if (FireState == ShipFireState.FireAtTarget)
primeCompartment = TargetCompartment;
// If ship is to the left of target, below will work
Vector2 distanceToDestination = primeCompartment.Position - CenterCompartment.Position;
float angleToEnemy = (float)Math.Atan2(distanceToDestination.Y, distanceToDestination.X);
angleToEnemy = MathHelper.WrapAngle(angleToEnemy);
CenterCompartment.Rotation = MathHelper.WrapAngle(CenterCompartment.Rotation);
float portBatteryAngle = MathHelper.WrapAngle(CenterCompartment.Rotation - Helpers.RightAngle);
float starboardBatteryAngle = MathHelper.WrapAngle(CenterCompartment.Rotation + Helpers.RightAngle);
float allowance = 0.005f;
bool portIsClosest = false;
switch (primaryFacing)
{
case PreferredFacing.None:
// If port battery not facing enemy
if (angleToEnemy > (MathHelper.WrapAngle(portBatteryAngle + allowance))
|| angleToEnemy < (MathHelper.WrapAngle(portBatteryAngle - allowance)))
{
// And starboard battery not facing either
if (angleToEnemy > (MathHelper.WrapAngle(starboardBatteryAngle + allowance))
|| angleToEnemy < (MathHelper.WrapAngle(starboardBatteryAngle - allowance)))
MoveState = Ship.ShipMoveState.NoMoveRotate;
else
MoveState = ShipMoveState.AwaitFurtherOrders;
}
else
{
portIsClosest = true;
MoveState = ShipMoveState.AwaitFurtherOrders;
}
if (MoveState == ShipMoveState.AwaitFurtherOrders)
{
float diff = 0f;
if (portIsClosest)
diff = angleToEnemy - portBatteryAngle;
else
diff = angleToEnemy - starboardBatteryAngle;
RotateShip(diff);
}
else if (MoveState == Ship.ShipMoveState.NoMoveRotate)
{
// Turn to port (if target is between 6 and 9 o'clock)
if (angleToEnemy < portBatteryAngle)
RotateShip(-MaxRotation / 100);
// Turn to starboard (if target is between 3 and 6 o'clock)
else if (angleToEnemy > starboardBatteryAngle)
RotateShip(MaxRotation / 100);
else
{
if (angleToEnemy > portBatteryAngle && angleToEnemy < starboardBatteryAngle)
{
// Turn to starboard (if target is between 9 and 12 o'clock)
if (angleToEnemy < CenterCompartment.Rotation)
RotateShip(MaxRotation / 100);
// Turn to port (if target is between 12 and 3 o'clock)
else
RotateShip(-MaxRotation / 100);
}
}
}
break;
}
Please let me know if you require any further information. Thank you very much for your assistance.
Instead of, for instance,
a < wrap(b-c)
use
0 < wrap(b-c-a)
or
0 > wrap(a-b+c)
This makes it a little less readable, but is the correct way to compare (supposedly small) angle differences.

Implement bouncing balls collision detection

I have a bouncing ball application and I have to extend it to prevent overlapping of the balls.
When ball overlaps another, they should move away as in real life.
I have to extend the given MoveBall method:
private void MoveBall()
{
prevX = x;
prevY = y;
x += xVelocity;
y += yVelocity;
// Is there too closed ball?
foreach (Ball ball in parentForm.balls)
{
distance = Math.Sqrt(Math.Pow((double)(ball.prevX - prevX), 2) +
Math.Pow((double)(ball.prevY- prevY), 2));
overlap = ((radius + ball.radius) - distance);// +ball.radius;
if (ball.id != this.id &&
ball.id != lastID &&
overlap > 0)
{
lastID = this.id;
if (xVelocity > 0) // roading right
{
xVelocity = -xVelocity;
x -= xVelocity - ball.xVelocity;
}
else if (xVelocity <= 0) // roading left
{
xVelocity = -xVelocity;
x += xVelocity + ball.xVelocity;
}
if (yVelocity > 0)
{ // going up
yVelocity = -yVelocity;
y -= yVelocity - ball.yVelocity;
}
else if (yVelocity <= 0) // down
{
yVelocity = -yVelocity;
y += yVelocity + ball.yVelocity;
}
}
}
// ***********************************************
// ***************** END MY CODE *****************
if (x > parentForm.Width - 10 - (radius) || x < 0)
{
if (x < 0) x = 0;
if (x > parentForm.Width - 10) x = parentForm.Width - 10 - radius;
xVelocity = -xVelocity;
}
if (y > parentForm.Height - 40 - (radius) || y < 0)
{
if (y < 0) y = 0;
if (y > parentForm.Height - 40) y = parentForm.Height - 40 - (radius);
yVelocity = -yVelocity;
}
}
x,y, xVelocity, yVelocity, radius, prevX, prevY declared as int.
overlap, distance as double.
When 2 overlap, they are getting stuck. Why?
Unfortunately, I can't upload all source code because there are lot of modules.
I'm using Visual C# Express 2010.
As no Question is asked explicitly, I will assume the question "Why are the balls sticking together?"
You have only shown one loop in source code, that's not enough ;-) To check all possible collisions, you need to check n*(n-1)/2 possible collisions. That is normally done with two loops. You have to put in careful measures to avoid handling the same collision twice.
The reason that your balls get stuck is that you handle the same collision multiple times. For example two balls colliding exactly horizontal: The left one has velocity 5 and x-position of 100. The other one shall have a position of 110 and velocity of -6. When the collision happens:
x is set to 105.
Collision detected: x is set to 104 and velocity to -5.
The other Ball handles the same collision:
He moves according to his velocity to position 104.
Collision handling: His velocity becomes 6 and position becomes 105.
The balls were at 100 and 110 resp. and have been moved to 104 and 105. While the velocities are now pointing away from each other, the collision handling in the following step will invert them again. So the positions are close together and the velocities are changing sign every frame. The balls seem "stuck to each other".
I hope the answer helps you to understand your problem. For a better implementation of an elastic collision (that handles each collision exactly once) look here: Ball to Ball Collision - Detection and Handling
Having stumbled upon similar issues when I made my first attempts at collision detection algorithms, I'll try to describe what I think is the problem here.
Maybe the balls move fast enough so that, before collision is even detected by your code, they are already partially "inside" each other. When collision detection comes and notices that, it does what it's supposed to do: change the planned trajectories of the objects according to the details of the collision that just happened. The problem is that, because these objects got sort-of "merged" before collision detection caught them, they can't get unstuck because collision detection is fired again, trapping them with each other.
If this is the source of the problem, then maybe the above code would work with a small enough velocity vector. Of course, that's not a real solution, but if it does work for very small velocities, it probably confirms my hypothesis and you have some idea regarding how to proceed.

Problem with bounds of an angle

Heads up: Even though this problem arose while I was working with Unity, it has nothing specific to Unity, and is more about programming logic, so please don't shy away.
I'm using Unity and rotating an object by script. The thing is, if I rotate it to, say, 180 degrees, the object does not rotate exactly to that much and tends to stop at between 179 and 181 degrees. So, to check if rotation is complete I check if the rotation angle is targetAngle +/- 1, which works.
I check using
if (transform.eulerAngles.z > lowerLimit && transform.eulerAngles.z < upperLimit)
where
lowerLimit = targetAngle-1;
upperLimit = targetAngle + 1;
Now, the problem arises when the targetAngle is 0. In this case, my script checks if rotation angle is between -1 and 1. But, -1 should really be 359, so it needs to check if the angle lies between 359 and 1.
How can I implement this?
In other words, I guess I'm asking how to implement a wrap-around number system.
EDIT
Found one work-around. If targetAngle is 0, I treat is specially. It works, but isn't the most elegant.
if (targetAngle == 0.0)
{
if ((transform.eulerAngles.z > 359.0 && transform.eulerAngles.z <= 360.0) || (transform.eulerAngles.z >= 0.0 && transform.eulerAngles.z <= 1))
{
rotate = false;
}
}
else
{
if (transform.eulerAngles.z > targetAngle - 1 && transform.eulerAngles.z < targetAngle + 1)
{
rotate = false;
}
}
You could do ...
lowerLimit = (targetAngle % 360) + 359; //360 - 1;
upperLimit = (targetAngle % 360) + 361; //360 + 1;
if (((transform.eulerAngles.z + 360) % 360) > lowerLimit
&& ((transform.eulerAngles.z + 360) % 360) < upperLimit)
This moves the check away from the zero and you wouldn't have to deal with positive/negative checking.
EDIT
The % operator on the targetAngle restricts the rotating to +/-359 degrees, so a target angle of 721 would come down to 1, and a target angle of -359 would come down to 1. This should do nicely for all cases I think.
EDIT 2
To fix the last case you mentioned in your comment, I guess you'd need to apply the same wrapping logic to your transform.eulerAngles.z values. Probably best to put this wrapping in an extra function now, so try this:
int wrapNumber(int input) // replace int with whatever your type is
{
// this will always return an angle between 0 and 360:
// the inner % 360 restricts everything to +/- 360
// +360 moves negative values to the positive range, and positive ones to > 360
// the final % 360 caps everything to 0...360
return ((input % 360) + 360) % 360;
}
lowerLimit = wrapNumber(targetAngle) + 359; //360 - 1;
upperLimit = wrapNumber(targetAngle) + 361; //360 + 1;
if (wrapNumber(transform.eulerAngles.z) + 360 > lowerLimit
&& wrapNumber(transform.eulerAngles.z) + 360 < upperLimit)
Depending on how often you need to use this, checking for some cases might remove unneeded overhead. For example, the final % 360 within wrapNumber is only needed if the input was positive. If you're calling this ten times per minute it probably won't matter. If you're calling it a hundred times per second, you may want to check how it performs in this situation.
This may be an old thread but after looking at many different snippets all trying to deal with Wrapping I found that Unity has a nice builtin function that simply takes care of business, At least in my case that the end result i required was a lerp so i only had to change it to LerpAngle and it returned a solid result.
Mathf.LerpAngle is your friend.. solved all my issues with popping etc..
http://docs.unity3d.com/ScriptReference/Mathf.LerpAngle.html

C# Brickbreaker game, get brick to dissapear when hit

I have an array for the x co-ordinate of the bricks, an array for the y co-ordinates of the bricks and an array to show weather it is visible. My code reads like this:
if (Visible[0] == 1)
{
paper.DrawRectangle(pen, BrickX[0], BrickY[0], 50, 10);
}
and for my collision for the ball to hit the brick, the code reads:
if (Visible[0] == 1)
{
if ((x >= BrickX[0]) && (x <= BrickX[0] + 50))
{
if ((y >= picDisplayBat.Height - 190) && (y <= BrickY[0] + 10))
yChange = -yChange;
Visible[0] = 0;
This does not seem to work as when the ball comes close the brick, the brick dissapears before the collision has happened and the ball goes straight through it. any suggestions?
You're missing brackets around your second if test.
Regardless of the outcome of the test the brick gets set to invisible. Your code is effectively this:
if ((y >= picDisplayBat.Height - 190) && (y <= BrickY[0] + 10))
{
yChange = -yChange;
}
Visible[0] = 0;
You want this:
if ((y >= picDisplayBat.Height - 190) && (y <= BrickY[0] + 10))
{
yChange = -yChange;
Visible[0] = 0;
}
I won't answer your question exactly, but this might help.
It's kind of unobvious what your code does now. Why always [0]?
Just create a class called Brick and a class called Ball:
class Brick {
public double x, y;
public double size;
public bool visible;
};
class Ball {
public double x, y;
public double size;
};
Now introduce the member in the Brick called bool Intersects(Ball ball), which yields true if the ball intersects with the brick (you could simply check this by their bounding boxes).
Now your checks become extremely simple (I assume you have an array of bricks somewhere called bricks).
foreach (Brick brick in bricks) {
if (brick.visible && brick.Intersects(ball)) {
// Hide the brick
brick.visible = false;
}
}
In short, I think you are doing your steps out of order.
Your second routine is perhaps doing too much. It should only determine a collision, and return an identifier for the target of the collision. Then a consuming routine uses that information to adjust projectile trajectory (reflect it back) and finally turn-off the visibility.
By the way, collision detection is a popular venue for what is known as "double-dispatch" invocations. Consider using the visitor pattern (ala Gang of Four) as a way to code this up.

C# 2D collision detection problem

I am stuck trying to figure out how to alter my collision detection to work correctly, i got all my wall objects stacked inside a List and then when the player moves i loop thru each wall object and call the DetectCollision method, this returns true or false depending on if the object is inside the wall or not.
Wall detect collision (X- and Y-coordinate is the position of the wall)
public bool DetectCollision(float x, float y)
{
if ((x >= this.XCoordinate && x <= (this.XCoordinate + this.BlockWidth)) && (y >= this.YCoordinate && y <= (this.YCoordinate + this.BlockHeight)))
return true;
else
return false;
}
So in my player function when the player tries to move, i add the movement to a temporary X,Y coordinate and check if those Collide against the wall, if they do nothing happens, otherwise i move the player.
But i have noticed that it doesn't work as it should be, if i add a piece of wall inside of the gamefield it only checks the bottom right corner for collision detection?
Player movement method:
float x, y;
if (direction == Direction.E)
{
x = LiveObjects.player.XCoordinate - MovementSpeed;
y = LiveObjects.player.YCoordinate;
}
else if (direction == Direction.W)
{
x = LiveObjects.player.XCoordinate + MovementSpeed;
y = LiveObjects.player.YCoordinate;
}
else if (direction == Direction.N)
{
x = LiveObjects.player.XCoordinate;
y = LiveObjects.player.YCoordinate - MovementSpeed;
}
else
{
x = LiveObjects.player.XCoordinate;
y = LiveObjects.player.YCoordinate + MovementSpeed;
}
if (GameMechanics.DetectWallCollision(x, y) || GameMechanics.DetectWallCollision((x + LiveObjects.player.BlockWidth), (y + LiveObjects.player.BlockHeight))
{
OnPlayerInvalidMove(null, new PlayerEventArgs());
return;
}
and the loop for DetectWallCollision is just:
foreach (Wall wall in LiveObjects.walls)
{
if (wall.DetectCollision(x, y))
return true;
}
return false;
Any ideas?
I'm assuming there isn't anything in your world that is infinitely small (i.e. is the size of a pixel). To have true bounding box collision, you've got to consider the size of both objects, not just one.
boolean intersectsEntity(Entity e)
{
return (e.position.x <= position.x + size.x) &&
(e.position.y <= position.y + size.y) &&
(e.position.x + e.size.x >= position.x) &&
(e.position.y + e.size.y >= position.y);
}
That's of course assuming an Entity has a vector for its position and for its size. So size.x == width, and size.y == height.
There is something that disturbs me, you said that the DetectCollision method gets the position of the wall - but if I interpret your code correctly you hand to the DetectWallCollision the x and y parameter which is the position (after movement) of the player and hand that position down to the DetectCollision method...
have you debugged your code to see what coordinates are passed to the collision methods and traced the routes your if-statements are going?
if it is not possible to debug your code for whatever reason - write a trace file - I think the solution will fall into your lap ;)
Your east and west are the wrong way around. With a coordinate system of 0,0 at the top left, increasing positively as you move down or to the right, then a movement West would normally mean a movement left, which means decreasing values of X, and the reverse for East. You are doing the opposite.

Categories