Wednesday, March 22, 2017

xna - How do I make a jumping dolphin rotate realistically?


I want to program a dolphin that jumps and rotates like a real dolphin. Jumping is not the problem, but I don't know how to make the rotation. At the moment, my dolphin rotates a little weird. But I want that it rotates like a real dolphin does.


How can I improve the rotation?


public class Game1 : Microsoft.Xna.Framework.Game
{

GraphicsDeviceManager graphics;
SpriteBatch spriteBatch;
Texture2D image, water;
float Gravity = 5.0F;
float Acceleration = 20.0F;
Vector2 Position = new Vector2(1200,720);
Vector2 Velocity;
float rotation = 0;
SpriteEffects flip;
Vector2 Speed = new Vector2(0, 0);


public Game1()
{
graphics = new GraphicsDeviceManager(this);
Content.RootDirectory = "Content";
graphics.PreferredBackBufferWidth = 1280;
graphics.PreferredBackBufferHeight = 720;
}

protected override void Initialize()

{
base.Initialize();
}

protected override void LoadContent()
{
spriteBatch = new SpriteBatch(GraphicsDevice);
image = Content.Load("cartoondolphin");
water = Content.Load("background");
flip = SpriteEffects.None;

}

protected override void Update(GameTime gameTime)
{
float VelocityX = 0f;
float VelocityY = 0f;

float time = (float)gameTime.ElapsedGameTime.TotalSeconds;
KeyboardState kbState = Keyboard.GetState();
if(kbState.IsKeyDown(Keys.Left))

{
rotation = 0;
flip = SpriteEffects.None;
VelocityX += -5f;
}

if(kbState.IsKeyDown(Keys.Right))
{
rotation = 0;
flip = SpriteEffects.FlipHorizontally;

VelocityX += 5f;
}

// jump if the dolphin is under water
if(Position.Y >= 670)
{
if (kbState.IsKeyDown(Keys.A))
{
if (flip == SpriteEffects.None)
{

rotation += 0.01f;
VelocityY += 40f;
}
else
{
rotation -= 0.01f;
VelocityY += 40f;
}
}
}

else
{
if (flip == SpriteEffects.None)
{
rotation -= 0.01f;
VelocityY += -10f;
}
else
{
rotation += 0.01f;

VelocityY += -10f;
}
}

float deltaY = 0;
float deltaX = 0;

deltaY = Gravity * (float)gameTime.ElapsedGameTime.TotalSeconds;

deltaX += VelocityX * (float)gameTime.ElapsedGameTime.TotalSeconds * Acceleration;

deltaY += -VelocityY * (float)gameTime.ElapsedGameTime.TotalSeconds * Acceleration;

Speed = new Vector2(Speed.X + deltaX, Speed.Y + deltaY);
Position += Speed * (float)gameTime.ElapsedGameTime.TotalSeconds;
Velocity.X = 0;

if (Position.Y + image.Height/2 > graphics.PreferredBackBufferHeight)
Position.Y = graphics.PreferredBackBufferHeight - image.Height/2;

base.Update(gameTime);

}

protected override void Draw(GameTime gameTime)
{
GraphicsDevice.Clear(Color.CornflowerBlue);
spriteBatch.Begin();
spriteBatch.Draw(water, new Rectangle(0, graphics.PreferredBackBufferHeight -100, graphics.PreferredBackBufferWidth, 100), Color.White);
spriteBatch.Draw(image, Position, null, Color.White, rotation, new Vector2(image.Width / 2, image.Height / 2), 1, flip, 1);
spriteBatch.End();


base.Draw(gameTime);
}
}



The code now works almost perfectly. I introduced the bool variable direction and made some tests with the angle. Jumping works now as it should. But I couldn't change two things:


1)At the beginning(you can see it in the video), the dolphin always looks up. I want that it looks at the right side, if possible. I tried to change the rotation but it didn't worked.


2)The second thing is a little bit strange. If I change the direction from left to right or right to left, the dolphin is rotated in the wrong direction during a short moment. How can I fix that? I made a new video: http://www.myvideo.de/watch/8881567


    public class Game1 : Microsoft.Xna.Framework.Game
{

GraphicsDeviceManager graphics;
SpriteBatch spriteBatch;
Texture2D image, water;
float Gravity = 5.0F;
float Acceleration = 20.0F;
Vector2 Position = new Vector2(1200,720);
Vector2 Velocity;
float rotation = 0;
SpriteEffects flip;
Vector2 Speed = new Vector2(0, 0);

bool direction = false;
Vector2 prevPos;

public Game1()
{
graphics = new GraphicsDeviceManager(this);
Content.RootDirectory = "Content";
graphics.PreferredBackBufferWidth = 1280;
graphics.PreferredBackBufferHeight = 720;
}


protected override void Initialize()
{
base.Initialize();
}

protected override void LoadContent()
{
spriteBatch = new SpriteBatch(GraphicsDevice);
image = Content.Load("cartoondolphin");

water = Content.Load("background");
flip = SpriteEffects.None;
}

protected override void Update(GameTime gameTime)
{
float VelocityX = 0f;
float VelocityY = 0f;

float time = (float)gameTime.ElapsedGameTime.TotalSeconds;

KeyboardState kbState = Keyboard.GetState();
if(kbState.IsKeyDown(Keys.Left))
{
flip = SpriteEffects.FlipHorizontally;
VelocityX += -5f;
direction = false;
}

if(kbState.IsKeyDown(Keys.Right))
{

flip = SpriteEffects.None;
VelocityX += 5f;
direction = true;
}

if (direction == false)
{
rotation = -((float)Math.Atan2(Position.X - prevPos.X, Position.Y - prevPos.Y) + MathHelper.PiOver2);
prevPos = Position;
}

if (direction == true)
{
rotation = -((float)Math.Atan2(Position.X - prevPos.X, Position.Y - prevPos.Y) + MathHelper.Pi + MathHelper.PiOver2);
prevPos = Position;
}

// jump if the dolphin is under water
if(Position.Y >= 670)
{
if (kbState.IsKeyDown(Keys.A))

{
if (flip == SpriteEffects.None)
{
VelocityY += 40f;
}
else
{
VelocityY += 40f;
}
}

}
else
{
if (flip == SpriteEffects.None)
{
VelocityY += -10f;
}
else
{
VelocityY += -10f;

}
}

float deltaY = 0;
float deltaX = 0;

deltaY = Gravity * (float)gameTime.ElapsedGameTime.TotalSeconds;

deltaX += VelocityX * (float)gameTime.ElapsedGameTime.TotalSeconds * Acceleration;
deltaY += -VelocityY * (float)gameTime.ElapsedGameTime.TotalSeconds * Acceleration;


Speed = new Vector2(Speed.X + deltaX, Speed.Y + deltaY);

Position += Speed * (float)gameTime.ElapsedGameTime.TotalSeconds;

Velocity.X = 0;

if (Position.Y + image.Height/2 > graphics.PreferredBackBufferHeight)
Position.Y = graphics.PreferredBackBufferHeight - image.Height/2;


base.Update(gameTime);
}

protected override void Draw(GameTime gameTime)
{
GraphicsDevice.Clear(Color.CornflowerBlue);
spriteBatch.Begin();
spriteBatch.Draw(water, new Rectangle(0, graphics.PreferredBackBufferHeight -100, graphics.PreferredBackBufferWidth, 100), Color.White);
spriteBatch.Draw(image, Position, null, Color.White, rotation, new Vector2(image.Width / 2, image.Height / 2), 1, flip, 1);
spriteBatch.End();


base.Draw(gameTime);
}
}

Answer




A jumping dolphin's rotation is related to the direction it's moving.


dolphin trajectories


The blue is water.
The dots are dolphin positions at each time tick.

The arrows are the direction the dolphin is rotated to face.


That bottom one is a radioactive dolphin with gravity powers.



You could get the same effect with something like this:


function dolphin.onPositionChange ()
dolphin.sprite.rotation = directionFromTo(dolphin.prevPos, dolphin.pos)
prevPos = pos
end

That's only illustrative pseudocode of course. Here's what it does in English:




Every time the dolphin's position changes
The angle from its previous position to new position becomes its rotation
The current position is saved as the old one, for use again next step

Finding the direction from point A to point B is a little bit of geometry. XNA might have a function that does this for you. If it does, use that!



If it doesn't (or if you just want to understand what's going on), here's the geometry for you:


geometry calculation of angle given opposite and adjacent


Love2D (my favourite game framework) doesn't have a function to calculate the direction from point to point. Here's how I made one:



-- Vector direction
-- Angle in radians, clockwise from up i.e. straight up is 0, right is
-- pi/2, down is pi, etc.
function directionFromTo(a,b)
return math.atan2( (b.x - a.x) , (b.y - a.y) )
end

-- Test; should print 135
a = {x = 0, y = 0}
b = {x = 1, y = -1}

print( directionFromTo(a,b) / math.pi*180) -- (division to convert to degrees)

No comments:

Post a Comment

Simple past, Present perfect Past perfect

Can you tell me which form of the following sentences is the correct one please? Imagine two friends discussing the gym... I was in a good s...