I'm trying to create a OpenGL C# project to zoom in and out for a scene. I want to enter and exit fom zoom mode when I press the Z key. And then, when I'm in zoom mode ( Z key pressed first time ) to use the mouse wheel to zoom in / out the scene. When I finished the zoom action, I want to be able to exit the zoom mode ( press Z again ) and then the mouse wheel will stop zooming my scene. Thanks for help!
protected override void OnUpdateFrame(FrameEventArgs e)
{
base.OnUpdateFrame(e);
Matrix4 lookat = Matrix4.LookAt(eyeVector, targetVector, upVector);
GL.MatrixMode(MatrixMode.Modelview);
GL.LoadMatrix(ref lookat);
KeyboardState keyboard = Keyboard.GetState();
if (keyboard[Key.Z] && !keyboard.Equals(lastKeyPress))
{
}
if (keyboard[Key.Z] && !keyboard.Equals(lastKeyPress))
{
}
lastKeyPress = keyboard;
}
This is the function for zoomOut:
public Vector3 zoomOut(Vector3 actual)
{
if(zoomLimits[1]<=actual.X || zoomLimits[1] <= actual.Y || zoomLimits[1] <= actual.Z) {
Console.WriteLine("Limita de zoomOut a fost atinsa!");
return actual;
}
Vector3 nou = new Vector3(actual.X + 5, actual.Y + 5, actual.Z + 5);
return nou;
}
Here I just change the eyeVector from the lookat matrix. But I have some limits. If I exceed those limits the scene will dissapear. Do you have any ideas to solve this? I dont want limits for zoom.
There's only so much help I can give without seeing your full code, but have you checked the clipping planes? That sounds like a textbook case of having a clipping plane too close; check your perspective matrix.
When you call Matrix4.CreatePerspectiveFieldOfView, increase depthFar, that should fix your issue.
Related
I have a sprite, Player. I update Player's position the following way:
_position += (_direction * _velocity) * (float)gameTime.ElapsedGameTime.TotalSeconds;
Where _position, _direction and _velocity are Vector2s.
I have a simple collider(BoxCollider) which simply generates a rectangle (BoundingBox) given the position and dimensions of the collider.
In Initialize(), I create a new List<BoxCollider> and fill it with colliders for the level.
On Update(), I pass the list to Player to check for collisions.
The collision check method:
public void CheckPlatformCollision(List<BoxCollider> colliders, GameTime gameTime)
{
var nextPosition = _position + (_direction * _velocity) * (float)gameTime.ElapsedGameTime.TotalSeconds;
Rectangle playerCollider = new Rectangle((int)nextPosition.X, (int)nextPosition.Y, BoundingBox.Width, BoundingBox.Height);
foreach(BoxCollider collider in colliders)
{
if(playerCollider.Intersects(collider.BoundingBox))
{
nextPosition = _position;
}
}
Position = nextPosition;
}
Right now, every way I've tried to implement gravity has failed. If Player is dropped from too high, nextPosition becomes too far away from Player and leaves it stuck in mid-air.
I'm also having problems with horizontal collisions as well, the issue being similar: Player stops too soon, leaving a space between. Sometimes I've had Player stick to the side of the collider.
What I would like to know is:
How do I properly implement gravity & jumping with _position, _direction, and _velocity? How do I properly handle collisions both horizontally and vertically?
For gravity, add this just before you update _position:
_velocity += gravity * (float)gameTime.ElapsedGameTime.TotalSeconds;
Where gravity is something like new Vector2(0, 10).
For jumping, you need to set the vertical component of the velocity when the player presses the jump button:
if (jumpPressed && jumpAvailable)
{
_velocity.Y = -10; // Numbers are example. You need to adjust this
jumpAvailable = false;
}
And you need to reset jumpAvailable when the player touches the floor.
Colliding is a much complicated thing. But if you look for "XNA implement collision" on the internet you will find a lot of answers.
There are many ways. One of them is pushing back the player to the border of the boxcollider, instead of not letting him move, like you did in your code. The code would be:
if (IntersectsFromTop(playerCollider, collider.BoundingBox))
{
_position.Y = collider.BoundingBox.Y - BoundingBox.Height;
}
else if (IntersectsFromRight(playerCollider, collider.BoundingBox))
{
_position.X = collider.BoundingBox.X + collider.BoundingBox.Width;
}
// And so on...
The helper methods can implemented like:
private static bool IntersectsFromTop(Rectange player, Rectangle target)
{
var intersection = Rectangle.Intersect(player, target);
return player.Intersects(target) && intersection.Y == target.Y && intersection.Width >= intersection.Height;
}
private static bool IntersectsFromRight(Rectange player, Rectangle target)
{
var intersection = Rectangle.Intersect(player, target);
return player.Intersects(target) && intersection.X + intersection.Width == target.X + target.Width && intersection.Width <= intersection.Height;
}
// And so on...
The rationnale behind that code can be explained with a picture:
In that picture width and height correspond to the intersection, in purple.
I made a simple test in xna 2D sprite and tried to move it with mouse, it works but if I move it a bit fast the sprite is lost on the way, I keep the left button pressed and when I'm back to the sprite the drag continues...
I wonder why if I moved a bit fast I lose my sprite???
Here is my move logic :
MouseState ms = Mouse.GetState();
if ((ButtonState.Pressed == Mouse.GetState().LeftButton) && myBall.RectObject.Intersects(new Rectangle(ms.X, ms.Y, 0, 0)))
{
myBall.RectObject = new Rectangle(ms.X - myBall.RectObject.Width / 2, ms.Y - myBall.RectObject.Height / 2, myBall.RectObject.Width, myBall.RectObject.Height);
}
I suggest handling it like this:
if the mouse is left-clicked on the ball (Using the if statement in the OP), then mark the ball as being dragged (A simple 'bool dragged;' on the object)
If the mouse is not left clicked at any time regardless of location, mark the ball as not being dragged.
If the ball is being dragged, jump to the mouse position (Using the code within the if block in the OP)
(All in the same function you are already using)
Edit: here's some sample code in case I didn't explain clearly
MouseState ms = Mouse.GetState();
if ((ButtonState.Pressed == Mouse.GetState().LeftButton))
{
if (myBall.RectObject.Intersects(new Rectangle(ms.X, ms.Y, 0, 0)))
{
myball.dragged = true;
}
}
else
{
myball.dragged = false;
}
if (myball.dragged)
{
myBall.RectObject = new Rectangle(ms.X - myBall.RectObject.Width / 2, ms.Y - myBall.RectObject.Height / 2, myBall.RectObject.Width, myBall.RectObject.Height);
}
I'm developing a 2D game with XNA game studio 4.0 and I need to make my "Hero" sprite shoot a shot sprite, which is a rectangle.
When I press left control to shoot, the shot is starting from my player. So far, it's ok, but the problem is that it never stops - its position never goes to theVoid.
Here is my code for shooting:
protected override void Update(GameTime gameTime)
{
if (Keyboard.GetState().IsKeyDown(Keys.LeftControl) && isShotted == false)
{
isShotted = true;
shotPosition = playerPosition;
}
if (isShotted == true && (shotPosition.X <= shotPosition.X+150) )
{
shotPosition.X += shotSpeed.X * (float)gameTime.ElapsedGameTime.TotalSeconds;
}
else
{
isShotted = false;
shotPosition = theVoid;
}
}
Some clarification:
playerPosition is my "Hero" sprite position.
theVoid is Vector2 (700,700), when I set shotPosition = theVoid the shot dissapears.
The shot never disapears because you are updating shotPosition.x every update. You are checking:
if (isShotted == true && (shotPosition.X <= shotPosition.X+150) )
And then inside that if you increment shotPosition.X:
shotPosition.X += shotSpeed.X * (float)gameTime.ElapsedGameTime.TotalSeconds;
One option to fix this would be to check shotPosition.X against the player position - per #jonhopkins comment. If the player can move near the same speed as the shot though, they could just follow it and then the shot would never disappear, this may or may not be what you want.
Your other option is to store the position of where the shot was fired, and compare something like:
if (isShotted == true && (shotPosition.initialX+150 >= shotPosition.currentX) )
Make sure you think about this in terms of how your players and objects move around the coordinate system though. If your player is always stationary in regards to the x-axis that could simplify things compared to if they can run around the screen..
I am working on a simple game where you click on square sprites before they disappear. I decided to get fancy and make the squares rotate. Now, when I click on the squares, they don't always respond to the click. I think that I need to rotate the click position around the center of the rectangle(square) but I am not sure how to do this. Here is my code for the mouse click:
if ((mouse.LeftButton == ButtonState.Pressed) &&
(currentSquare.Contains(mouse.X , mouse.Y )))
And here is the rotation logic:
float elapsed = (float)gameTime.ElapsedGameTime.TotalSeconds;
RotationAngle += elapsed;
float circle = MathHelper.Pi * 2;
RotationAngle = RotationAngle % circle;
I am new to Xna and programming in general, so any help is appreciated.
Thanks a lot,
Bill
So you're trying to determine if a point is in a rectangle, but when the rectangle is rotated?
The Contains() method will only work if the current rotation is 0 (I guess currentSquare is a rectangle representing the image position without rotation?).
What you will have to do is do the opposite rotation of the image on the mouse coordinates (the mouse coordinates should rotate around the origin of your image), then calculate if the new position is within currentSquare. You should be able to do all of this using vectors.
(Untested)
bool MouseWithinRotatedRectangle(Rectangle area, Vector2 tmp_mousePosition, float angleRotation)
{
Vector2 mousePosition = tmp_mousePosition - currentSquare.Origin;
float mouseOriginalAngle = (float)Math.Atan(mousePosition.Y / mousePosition.X);
mousePosition = new Vector2((float)(Math.Cos(-angleRotation + mouseOriginalAngle) * mousePosition.Length()),
(float)(Math.Sin(-angleRotation + mouseOriginalAngle) * , mousePosition.Length()));
return area.Contains(mousePosition);
}
If you dont need pixel pefect detection you can create bounding sphere for each piece like this.
var PieceSphere = new BoundingSphere()
{
Center =new Vector3(new Vector2(Position.X + Width/2, Position.Y + Height/2), 0f),
Radius = Width / 2
};
Then create another bounding sphere around mouse pointer.For position use mouse coordinates and for radius 1f. Because mouse pointer will be moving it will change its coordinates so you have to also update the sphere's center on each update.
Checking for clicks would be realy simple then.
foreach( Piece p in AllPieces )
{
if ((mouse.LeftButton == ButtonState.Pressed) && p.BoundingSphere.Intersects(MouseBoundingSphere))
{
//Do stuff
}
}
If you are lazy like me you could just do a circular distance check.
Assuming mouse and box.center are Vector2
#gets us C^2 according to the pythagorean Theorem
var radius = (box.width / 2).squared() + (box.height / 2).square
#distance check
(mouse - box.center).LengthSquared() < radius
Not perfectly accurate but the user would have a hard time noticing and inaccuracies that leave a hitbox slightly too large are always forgiven. Not to mention the check is incredibly fast just calculate the radius when the square is created.
i just got into game development and XNA and was following a tutorial and decided to try and add in a bound area for a floor. In the tutorial the sprite could move freely and i wanted to have a stopping point for it, so i added a statement to part of the input method
if (aCurrentKeyboardState.IsKeyDown(Keys.Down) == true)
{
if (this.Position.Y == 420)
{
MOVE_DOWN = 0;
mDirection.Y = MOVE_DOWN;
}
else
{
mSpeed.Y = PLAYER_SPEED;
MOVE_DOWN = 1;
mDirection.Y = MOVE_DOWN;
}
}
MOVE_DOWN is my variable for the y change, if it = 0, there is no movement, 1 it moves down, -1 it moves up.
this worked only if the position of the bounds(420) was equal to the position that my sprite started out at, other than that it doesnt work.
i think its because the position isnt updating correctly. i dont know ive tried a lot of things and am pretty new with XNA and game development. Any help would be greatly appreciated.
Here is the update method for my player sprite
public void Update(GameTime theGameTime)
{
KeyboardState aCurrentKeyboardState = Keyboard.GetState();
UpdateMovement(aCurrentKeyboardState);
mPreviousKeyboardState = aCurrentKeyboardState;
base.Update(theGameTime, mSpeed, mDirection);
}
and here is the update for the base class
public void Update(GameTime theGameTime, Vector2 theSpeed, Vector2 theDirection)
{
Position += theDirection * theSpeed * (float)theGameTime.ElapsedGameTime.TotalSeconds;
}
If 'Position' is a Vector2, then it is using floats of the X & Y components.
For practical purposes, a float will rarely equal a whole number due to floating point rounding errors.
if (this.Position.Y == 420)//will never return true
should be changed to:
if (this.Position.Y < 420)
{
this.Position = 420;
//other stuff you have
}