When I press left or up arrow keys the sprite gets out of the window/screen. My code:
Texture2D m_PlayerShipTex;
Rectangle m_PlayerShipHitBox;
Vector2 m_PlayerShipPos = new Vector2(400, 486);
Vector2 m_PlayerShipOrigin;
int m_PlayerShipCurrentFrame = 1;
int m_PlayerShipFrameWidth = 62;
int m_PlayerShipFrameHeight = 64;
float m_Timer = 0f;
float m_Interval = 100;
public void LoadContent(ContentManager Content)
{
m_PlayerShipTex = Content.Load<Texture2D>(".\\gameGraphics\\gameSprites\\playerShip\\playerShipSpriteSheet");
}
public void Update(GameTime gameTime)
{
m_PlayerShipHitBox = new Rectangle(m_PlayerShipCurrentFrame * m_PlayerShipFrameWidth, 0, m_PlayerShipFrameWidth, m_PlayerShipFrameHeight);
m_PlayerShipOrigin = new Vector2(m_PlayerShipHitBox.X / 2, m_PlayerShipHitBox.Y / 2);
MouseState m_MouseState = Mouse.GetState();
KeyboardState m_KeyboardState = Keyboard.GetState();
m_Timer += (float)gameTime.ElapsedGameTime.Milliseconds;
if (m_Timer > m_Interval)
{
m_PlayerShipCurrentFrame++;
m_Timer = 0f;
}
if (m_PlayerShipCurrentFrame == 2)
{
m_PlayerShipCurrentFrame = 0;
}
m_PlayerShipHitBox = new Rectangle(m_PlayerShipCurrentFrame * m_PlayerShipFrameWidth, 0, m_PlayerShipFrameWidth, m_PlayerShipFrameHeight);
m_PlayerShipOrigin = new Vector2(m_PlayerShipHitBox.Width / 2, m_PlayerShipHitBox.Height / 2);
if (m_KeyboardState.IsKeyDown(Keys.Right))
{
m_PlayerShipPos.X += 3;
}
if (m_KeyboardState.IsKeyDown(Keys.Left))
{
m_PlayerShipPos.X -= 3;
}
if (m_KeyboardState.IsKeyDown(Keys.Down))
{
m_PlayerShipPos.Y += 3;
}
if (m_KeyboardState.IsKeyDown(Keys.Up))
{
m_PlayerShipPos.Y -= 3;
}
if (m_PlayerShipPos.X <= 0)
{
m_PlayerShipPos.X = 0;
}
if (m_PlayerShipPos.X + m_PlayerShipTex.Width >= 1141)
{
m_PlayerShipPos.X = 1141 - m_PlayerShipTex.Width;
}
if (m_PlayerShipPos.Y <= 0)
{
m_PlayerShipPos.Y = 0;
}
if (m_PlayerShipPos.Y + m_PlayerShipTex.Height >= 620)
{
m_PlayerShipPos.Y = 620 - m_PlayerShipTex.Height;
}
}
public void Draw(SpriteBatch spriteBatch)
{
spriteBatch.Draw(m_PlayerShipTex, m_PlayerShipPos, m_PlayerShipHitBox, Color.White, 0f, m_PlayerShipOrigin, 1.0f, SpriteEffects.None, 0);
}
I don't know what could be wrong, my window is 800x600 but if I set m_PlayerShipTex.Width >= 800 I can get only to half of the screen, that's why I'm using 1141. Same goes for window height... What am I doing wrong and why the ship's out of the "reacheable" area?
To fix the original problem, use this draw function:
public void Draw(SpriteBatch spriteBatch)
{
spriteBatch.Draw(m_PlayerShipTex, m_PlayerShipPos, m_PlayerShipHitBox, Color.White, 0f, Vector2.Zero, 1.0f, SpriteEffects.None, 0);
}
This will use (0,0) as the origin for drawing; which means that your "edge" calculations will work as originally expected. If you want the centered origin, then you need to account for that adjustment when calculating the edges.
The only reason to change the origin is to make rotation easier; in general, you can just use Vector2.Zero.
Related
First, sorry for my bad English, English is not my native language.
I have a problem when I draw a Texture2D in screen. When I move a Texture in screen,
colors in border of the texture flickers. This is my code:
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using SharpDX.Direct2D1.Effects;
using System;
using System.Collections.Generic;
namespace PixelTest
{
public class Drawable
{
public Texture2D Image;
public Point Position;
public Point Size;
public Rectangle Source;
}
public class MainGame : Game
{
//PRIVATE READ-ONLY VARIABLES
private readonly GraphicsDeviceManager _graphics;
private readonly List<Drawable> _sprites;
//VARIABLES
private Texture2D _screen;
private Texture2D _tiles;
private SpriteBatch _spriteBatch;
private Point _camera;
private Point _scale;
private bool _byRect;
private KeyboardState _previousKeyState;
private BlendState _state;
private RasterizerState _rState;
//CONSTRUCTOR
public MainGame()
{
_graphics = new GraphicsDeviceManager(this);
_sprites = new List<Drawable>();
_tiles = null;
_spriteBatch = null;
_camera = Point.Zero;
_scale = new Point(1, 1);
Content.RootDirectory = "Content";
IsMouseVisible = true;
IsFixedTimeStep = true;
//_graphics.PreferHalfPixelOffset = true;
_graphics.PreferMultiSampling= true;
//_graphics.SynchronizeWithVerticalRetrace = false;
_state = new BlendState()
{
AlphaBlendFunction = BlendFunction.Add,
AlphaSourceBlend = Microsoft.Xna.Framework.Graphics.Blend.Zero,
AlphaDestinationBlend = Microsoft.Xna.Framework.Graphics.Blend.Zero,
ColorBlendFunction= BlendFunction.Add,
ColorDestinationBlend = Microsoft.Xna.Framework.Graphics.Blend.Zero,
ColorSourceBlend = Microsoft.Xna.Framework.Graphics.Blend.One,
};
_rState = new RasterizerState()
{
MultiSampleAntiAlias = false
};
}
protected override void Initialize()
{
base.Initialize();
_sprites.Add(new Drawable
{
Image = _tiles,
Position = new Point(32, 64),
Size = new Point(8, 8),
Source = new Rectangle(0, 0, 8, 8)
});
}
protected override void LoadContent()
{
_spriteBatch = new SpriteBatch(GraphicsDevice);
_tiles = Content.Load<Texture2D>("i001_tiles");
_screen = new Texture2D(GraphicsDevice, _graphics.PreferredBackBufferWidth, _graphics.PreferredBackBufferHeight);
var colors = new Color[_graphics.PreferredBackBufferWidth * _graphics.PreferredBackBufferHeight];
for (var i = 0; i < _graphics.PreferredBackBufferWidth * _graphics.PreferredBackBufferHeight; i++)
colors[i] = Color.Black;
_screen.SetData(colors);
}
protected override void UnloadContent()
{
base.UnloadContent();
_tiles.Dispose();
_screen.Dispose();
}
protected override void Update(GameTime gameTime)
{
base.Update(gameTime);
if (GamePad.GetState(PlayerIndex.One).Buttons.Back == ButtonState.Pressed || Keyboard.GetState().IsKeyDown(Keys.Escape))
Exit();
var sKey = Keyboard.GetState();
var translateUnit = Point.Zero;
var scaleUnit = 0;
if (sKey.IsKeyDown(Keys.A))
translateUnit.X = -1;
else if (sKey.IsKeyDown(Keys.D))
translateUnit.X = 1;
else
translateUnit.X = 0;
if (sKey.IsKeyDown(Keys.W))
translateUnit.Y = -1;
else if (sKey.IsKeyDown(Keys.S))
translateUnit.Y = 1;
else
translateUnit.Y = 0;
if (sKey.IsKeyDown(Keys.F))
scaleUnit = -1;
else if (sKey.IsKeyDown(Keys.G))
scaleUnit = 1;
else
scaleUnit = 0;
if (sKey.IsKeyDown(Keys.U) && _previousKeyState.IsKeyUp(Keys.U))
_byRect = !_byRect;
var speed = translateUnit;// * 1;
_camera += speed;// RoundVec(speed);
_scale = new Point(4, 4);
_previousKeyState = sKey;
}
protected override void Draw(GameTime gameTime)
{
base.Draw(gameTime);
GraphicsDevice.Clear(Color.Blue);
_spriteBatch.Begin(SpriteSortMode.Deferred, _state, SamplerState.PointClamp, null, _rState, null, null);
//_spriteBatch.Draw(_screen, Vector2.Zero, Color.White);
foreach (var item in _sprites)
{
if (true)
{
var pos = item.Position;
var size = item.Size;
pos += _camera;
pos *= _scale;
size *= _scale;
var rect = new Rectangle(
(int)pos.X,
(int)pos.Y,
(int)size.X,
(int)size.Y);
_spriteBatch.Draw(
item.Image,
rect,
item.Source,
Color.White,
0F,
Vector2.Zero,
SpriteEffects.None,
0F);
}
else
{
var pos = item.Position;
//pos = RoundVec(pos * _scale) / _scale;
pos += _camera;
pos *= _scale;
_spriteBatch.Draw(
item.Image,
pos.ToVector2(),
item.Source,
Color.White,
0F,
Vector2.Zero,
_scale.ToVector2(),
SpriteEffects.None,
0F);
}
}
_spriteBatch.End();
}
}
}
I dont use floats values for camera position in this test and the flickers continue to happens. I dont know what is happen.
I tried various things, but nothing work.
Resources:
i001_tiles.png (no alpha values)
PrintScreens:
Maybe is just a ottical illusion, or a monitor problem, but the green part of texture merge with the blue background on camera movement, i dont know why.
In Movement Up:
This only occour with the colors different of white, when i movement camera to right, only the one pixel in green in the right merge with blue background.
I have not used any of this in class but its still a requirement on our project and I'm getting a bit squeezed.
Summarized: I have to do a pacman game in 2D using the monogame extension on Visual Studio, language is C# and the problem is occuring when I try to move the ghosts, creating the autonomous moviment, I can not wrap my head around a solution so, here's what I got:
float dist = Vector2.Distance(position.ToVector2(), targetPosition.ToVector2());
if (dist <= 1)
{
List<Direction> availableDirections = new List<Direction>();
foreach (var dir in Surroundings)
if (game1.Board.board[targetPosition.X / (Game1.outputTileSize) + dir.Value.X, targetPosition.Y / (Game1.outputTileSize) + dir.Value.Y] == ' ')
availableDirections.Add(dir.Key);
if (availableDirections.Count == 0) return;
availableDirections = availableDirections.OrderBy(dir =>
{
float distP1 = Vector2.Distance(targetPosition.ToVector2() + Surroundings[dir].ToVector2() * Game1.outputTileSize, game1.Player.position.ToVector2());
return distP1;
}).ToList();
direction = availableDirections[0];
direction = Surroundings[availableDirections.Count - 1];
targetPosition = targetPosition + availableDirections[direction].Game1.outputTileSize;
}
else
{
//Position is not the same, move the guy
Vector2 vec = targetPosition.ToVector2() - position.ToVector2();
vec.Normalize();
position = (position.ToVector2() + vec).ToPoint();
//Incrementar frame
if ((position.X + position.Y) % 4 == 0)
frame++;
if (frame > 1) frame = -1;
}
I've tried to instantiate a different kind of list still using the dictionary,but the issue is that it's not recognizing the position on the list as an integer and it clashes. Here's the rest of the code that's relevant to understand the way everything connects:
namespace MsPacMan
{
public class Ghost : DrawableGameComponent
{
//enumeradores
enum Orientation { Horizontal, Vertical }
enum Direction { Up, Down, Left, Right }
#region variables
private Texture2D texture;
private SpriteBatch spriteBatch;
private SoundEffect eatghostSound;
private Game1 game1;
private int ghostType;
private Board board;
private Orientation orientation;
public Point position, targetPosition, origin;
int enemyLives = 4;
int patrolSize;
int patrolPosition = 0;
int direction = 1;
int frame = 0;
public static int ghostValue = 200;
Dictionary<Direction, Vector2> ghostColor;
Dictionary<Direction, Point> Surroundings;
Direction gDirection = Direction.Up;
#endregion
#region Constructor
public Ghost(Game1 game, int x, int y, int ghostType) : base(game)
{
orientation = Game1.rnd.Next(2) > 0 ? Orientation.Horizontal : Orientation.Vertical;
texture = game.SpriteSheet;
spriteBatch = game.SpriteBatch;
this.ghostType = ghostType;
position.Y = y;
position.X = x;
targetPosition = position;
game1 = game;
board = game1.Board;
origin = targetPosition = position;
patrolSize = 2 + Game1.rnd.Next(4);
eatghostSound = game1.Content.Load<SoundEffect>("pacman_eatghost");
Surroundings = new Dictionary<Direction, Point>
{
[Direction.Up] = new Point(0, -1),
[Direction.Down] = new Point(0, 1),
[Direction.Left] = new Point(-1, 0),
[Direction.Right] = new Point(0, 1),
};
ghostColor = new Dictionary<Direction, Vector2>
{
[Direction.Right] = new Vector2(0, ghostType),
[Direction.Left] = new Vector2(2, ghostType),
[Direction.Up] = new Vector2(4, ghostType),
[Direction.Down] = new Vector2(6, ghostType),
};
}
#endregion
#region Properties
public Board Board => board;
#endregion
#region Methods
public override void Update(GameTime gameTime)
{
Rectangle pRect = new Rectangle(game1.Player.position, new Point(Game1.outputTileSize));
Rectangle EnemyArea = new Rectangle(((position.ToVector2()) * Game1.outputTileSize).ToPoint(), new Point(Game1.outputTileSize));
ChasePattern(ghostType);
if (EnemyArea.Intersects(pRect))
{
Pellet.GetPelletStatus();
if (Pellet.powerPellet)
{
this.Die();
}
else
{
game1.Player.Die();
}
}
}
//Draws the different types of ghosts
public override void Draw(GameTime gameTime)
{
Rectangle outRect = new Rectangle(position.X * Game1.outputTileSize, position.Y * Game1.outputTileSize, Game1.outputTileSize, Game1.outputTileSize);
Rectangle sourceRec = new Rectangle(((ghostColor[gDirection] + Vector2.UnitX * frame) * 16).ToPoint(), new Point(15));
Rectangle sourcePelletRec = new Rectangle(8 * 16, 0, 16, 15);
spriteBatch.Begin();
Pellet.GetPelletStatus();
if (!Pellet.powerPellet)
{
spriteBatch.Draw(texture, outRect, sourceRec, Color.White);
}
else
{
spriteBatch.Draw(texture, outRect, sourcePelletRec, Color.White);
}
spriteBatch.End();
}
public void Die()
{
eatghostSound.Play();
enemyLives--;
int n = 4 - enemyLives;
AssignGhostValue(n);
game1.Ghosts.Remove(this);
game1.Components.Remove(this);
position = targetPosition = origin;
game1.Ghosts.Add(this);
game1.Components.Add(this);
}
public void AssignGhostValue(int n)
{
ghostValue = ghostValue * n;
game1.Player.Score += ghostValue;
}
public void ChasePattern(int ghostType)
{
int ghosType = ghostType;
int blinky = 0, pinky = 1, inky = 2, clyde = 3;
if (ghosType == blinky)
{
ChaseAggressive();
}
else if (ghosType == pinky)
{
ChaseAmbush();
}
else if (ghosType == inky)
{
ChasePatrol();
}
else if (ghosType == clyde)
{
ChaseRandom();
}
}
public void ChaseAggressive()
{
//Blinky the red ghost is very aggressive in its approach while chasing Pac - Man and will follow Pac-Man once located
float dist = Vector2.Distance(position.ToVector2(), targetPosition.ToVector2());
if (dist <= 1)
{
List<Direction> availableDirections = new List<Direction>();
foreach (var dir in Surroundings)
if (game1.Board.board[targetPosition.X / (Game1.outputTileSize) + dir.Value.X, targetPosition.Y / (Game1.outputTileSize) + dir.Value.Y] == ' ')
availableDirections.Add(dir.Key);
if (availableDirections.Count == 0) return;
availableDirections = availableDirections.OrderBy(dir =>
{
float distP1 = Vector2.Distance(targetPosition.ToVector2() + Surroundings[dir].ToVector2() * Game1.outputTileSize, game1.Player.position.ToVector2());
return distP1;
}).ToList();
direction = availableDirections[0];
direction = Surroundings[availableDirections.Count - 1];
targetPosition = targetPosition + availableDirections[direction]. Game1.outputTileSize;
}
else
{
//Position is not the same, move the guy
Vector2 vec = targetPosition.ToVector2() - position.ToVector2();
vec.Normalize();
position = (position.ToVector2() + vec).ToPoint();
//Incrementar frame
if ((position.X + position.Y) % 4 == 0)
frame++;
if (frame > 1) frame = -1;
}
}
}
}
I am drawing a line in XNA which I want to never end. I also have a tool that moves forward in X-direction and a camera which is centered at this tool. However, when I reach the end of the viewport the lines are not drawn anymore. Here are some pictures to illustrate my problem:
At the start the line goes across the whole screen, but as my tool moves forward, we reach the end of the line.
Here are the method which draws the lines:
private void DrawEvenlySpacedSprites (Texture2D texture, Vector2 point1, Vector2 point2, float increment)
{
var distance = Vector2.Distance (point1, point2); // the distance between two points
var iterations = (int)(distance / increment); // how many sprites with be drawn
var normalizedIncrement = 1.0f / iterations; // the Lerp method needs values between 0.0 and 1.0
var amount = 0.0f;
if (iterations == 0)
iterations = 1;
for (int i = 0; i < iterations; i++) {
var drawPoint = Vector2.Lerp (point1, point2, amount);
spriteBatch.Draw (texture, drawPoint, Color.White);
amount += normalizedIncrement;
}
}
Here are the draw method in Game. The dots are my lines:
protected override void Draw (GameTime gameTime)
{
graphics.GraphicsDevice.Clear(Color.Black);
nyVector = nextVector (gammelVector);
GraphicsDevice.SetRenderTarget (renderTarget);
spriteBatch.Begin ();
DrawEvenlySpacedSprites (dot, gammelVector, nyVector, 0.9F);
spriteBatch.End ();
GraphicsDevice.SetRenderTarget (null);
spriteBatch.Begin (SpriteSortMode.Deferred, BlendState.AlphaBlend, null, null, null, null, camera.transform);
spriteBatch.Draw (renderTarget, new Vector2 (), Color.White);
spriteBatch.Draw (tool, new Vector2(toolPos.X - (tool.Width/2), toolPos.Y - (tool.Height/2)), Color.White);
spriteBatch.End ();
gammelVector = new Vector2 (nyVector.X, nyVector.Y);
base.Draw (gameTime);
}
Can anyone point me in the right direction here? I'm guessing it has to do with the viewport.width, but I'm not quite sure how to solve it. Thank you for reading!
I read this and thought it would be a fun exercise this morning, so I decided to write this for fun.
The implementation is pretty simple, keep adding lines at the end of each other until the last line is outside of the viewable area.
The following code will draw a line infinitely going right. As an added optimization, the lines on the left side of the screen are deleted as you pass them. You could easily make it retain the lines that were there previously, or also create lines going left as well. I will leave these exercises to you.
Take a look at the following Line class, which will define a single line on screen:
public class Line
{
Texture2D Texture;
Color Color;
public Vector2 PointA;
public Vector2 PointB;
float Width;
public Line(Vector2 pointA, Vector2 pointB, float width, Color color, Texture2D texture)
{
Texture = texture;
PointA = pointA;
PointB = pointB;
Width = width;
Color = color;
}
public void Draw(SpriteBatch spritebatch)
{
float angle = (float)Math.Atan2(PointB.Y - PointA.Y, PointB.X - PointA.X);
float length = Vector2.Distance(PointA, PointB);
spritebatch.Draw(Texture, PointA, null, Color, angle, Vector2.Zero, new Vector2(length, Width), SpriteEffects.None, 0);
}
}
I wrote the implementation inside of a game class, since I was speedcoding. You can see the Game class below:
public class Game1 : Microsoft.Xna.Framework.Game
{
GraphicsDeviceManager graphics;
SpriteBatch spriteBatch;
Camera Camera;
Texture2D LineTexture;
List<Line> Lines;
Random Random;
public Game1()
{
graphics = new GraphicsDeviceManager(this);
Content.RootDirectory = "Content";
}
protected override void Initialize()
{
base.Initialize();
}
protected override void LoadContent()
{
spriteBatch = new SpriteBatch(GraphicsDevice);
Camera = new Camera(GraphicsDevice.Viewport, 1f);
LineTexture = new Texture2D(GraphicsDevice, 1, 1);
LineTexture.SetData<Color>(new Color[] { Color.White });
Random = new Random();
Lines = new List<Line>();
}
protected override void UnloadContent()
{
}
protected override void Update(GameTime gameTime)
{
//handle input
KeyboardState kbState = Keyboard.GetState();
if (kbState.IsKeyDown(Keys.Escape))
this.Exit();
if (kbState.IsKeyDown(Keys.OemMinus))
Camera.Zoom -= 0.015f;
else if (kbState.IsKeyDown(Keys.OemPlus))
Camera.Zoom += 0.015f;
if (kbState.IsKeyDown(Keys.Up))
Camera.Move(new Vector2(0, -30));
else if (kbState.IsKeyDown(Keys.Down))
Camera.Move(new Vector2(0, 30));
if (kbState.IsKeyDown(Keys.Left))
Camera.Move(new Vector2(-30, 0));
else if (kbState.IsKeyDown(Keys.Right))
Camera.Move(new Vector2(30, 0));
//check if line is still in viewport - if not remove it
for (int i = 0; i < Lines.Count; i++)
{
if (Lines[i].PointB.X < Camera.Viewport.X)
{
Lines.RemoveAt(i);
i--;
}
}
//if there are no lines, create one to get started
if (Lines.Count == 0)
{
Vector2 p1 = new Vector2(Camera.Viewport.X, Random.Next(Camera.Viewport.Y + 50, Camera.Viewport.Height - 100));
Vector2 p2 = new Vector2(p1.X + Random.Next(5, 20), p1.Y + Random.Next(-5, 5));
Line line = new Line(p1, p2, 1, Color.Black, LineTexture);
Lines.Add(line);
}
//Check if we need to add some lines to the right of our last list item
while (Lines[Lines.Count - 1].PointB.X < Camera.Viewport.X + Camera.Viewport.Width)
{
Vector2 p1 = new Vector2(Lines[Lines.Count - 1].PointB.X, Lines[Lines.Count - 1].PointB.Y); ;
Vector2 p2 = new Vector2(p1.X + Random.Next(5, 20), p1.Y + Random.Next(-5, 5));
Line line = new Line(p1, p2, 1, Color.Black, LineTexture);
Lines.Add(line);
}
base.Update(gameTime);
}
protected override void Draw(GameTime gameTime)
{
GraphicsDevice.Clear(new Color(30, 90, 150));
spriteBatch.Begin(SpriteSortMode.Deferred, null, null, null, null, null, Camera.View);
foreach (Line line in Lines)
line.Draw(spriteBatch);
spriteBatch.End();
base.Draw(gameTime);
}
}
I've also included the Camera class for your convenience:
public class Camera
{
private const float zoomUpperLimit = 1.5f;
private const float zoomLowerLimit = 0.1f;
private float _zoom;
private Vector2 _pos;
private int ViewportWidth, ViewportHeight;
#region Properties
public float Zoom
{
get { return _zoom; }
set
{
_zoom = value;
if (_zoom < zoomLowerLimit)
_zoom = zoomLowerLimit;
if (_zoom > zoomUpperLimit)
_zoom = zoomUpperLimit;
}
}
public Rectangle Viewport
{
get
{
int width = (int)((ViewportWidth / _zoom));
int height = (int)((ViewportHeight / _zoom));
return new Rectangle((int)(_pos.X - width / 2), (int)(_pos.Y - height / 2), width, height);
}
}
public void Move(Vector2 amount)
{
_pos += amount;
}
public Vector2 Position
{
get { return _pos; }
set { _pos = value; }
}
public Matrix View
{
get
{
return Matrix.CreateTranslation(new Vector3(-_pos.X, -_pos.Y, 0)) *
Matrix.CreateScale(new Vector3(Zoom, Zoom, 1)) *
Matrix.CreateTranslation(new Vector3(ViewportWidth * 0.5f, ViewportHeight * 0.5f, 0));
}
}
#endregion
public Camera(Viewport viewport, float initialZoom)
{
_zoom = initialZoom;
_pos = Vector2.Zero;
ViewportWidth = viewport.Width;
ViewportHeight = viewport.Height;
}
}
I have a sprite called Tool that moves with a speed represented as a float and in a direction represented as a Vector2. When I click the mouse on the screen the sprite change its direction and starts to move towards the mouseclick. In addition to that I rotate the sprite so that it is facing in the direction it is heading. However, when I add a camera that is suppose to follow the sprite so that the sprite is always centered on the screen, the sprite won't move in the given direction and the rotation isn't accurate anymore. This only happens when I add the Camera.View in the spriteBatch.Begin(). I was hoping anyone could maybe shed a light on what I am missing in my code, that would be highly appreciated.
Here is the camera class i use:
public class Camera
{
private const float zoomUpperLimit = 1.5f;
private const float zoomLowerLimit = 0.1f;
private float _zoom;
private Vector2 _pos;
private int ViewportWidth, ViewportHeight;
#region Properties
public float Zoom
{
get { return _zoom; }
set
{
_zoom = value;
if (_zoom < zoomLowerLimit)
_zoom = zoomLowerLimit;
if (_zoom > zoomUpperLimit)
_zoom = zoomUpperLimit;
}
}
public Rectangle Viewport
{
get
{
int width = (int)((ViewportWidth / _zoom));
int height = (int)((ViewportHeight / _zoom));
return new Rectangle((int)(_pos.X - width / 2), (int)(_pos.Y - height / 2), width, height);
}
}
public void Move(Vector2 amount)
{
_pos += amount;
}
public Vector2 Position
{
get { return _pos; }
set { _pos = value; }
}
public Matrix View
{
get
{
return Matrix.CreateTranslation(new Vector3(-_pos.X, -_pos.Y, 0)) *
Matrix.CreateScale(new Vector3(Zoom, Zoom, 1)) *
Matrix.CreateTranslation(new Vector3(ViewportWidth * 0.5f, ViewportHeight * 0.5f, 0));
}
}
#endregion
public Camera(Viewport viewport, float initialZoom)
{
_zoom = initialZoom;
_pos = Vector2.Zero;
ViewportWidth = viewport.Width;
ViewportHeight = viewport.Height;
}
}
And here is my Update and Draw-method:
protected override void Update (GameTime gameTime)
{
float elapsed = (float)gameTime.ElapsedGameTime.TotalSeconds;
TouchCollection touchCollection = TouchPanel.GetState ();
foreach (TouchLocation tl in touchCollection) {
if (tl.State == TouchLocationState.Pressed || tl.State == TouchLocationState.Moved) {
//direction the tool shall move towards
direction = touchCollection [0].Position - toolPos;
if (direction != Vector2.Zero) {
direction.Normalize ();
}
//change the direction the tool is moving and find the rotationangle the texture must rotate to point in given direction
toolPos += (direction * speed * elapsed);
RotationAngle = (float)Math.Atan2 (direction.Y, direction.X);
}
}
if (direction != Vector2.Zero) {
direction.Normalize ();
}
//move tool in given direction
toolPos += (direction * speed * elapsed);
//change cameracentre to the tools position
Camera.Position = toolPos;
base.Update (gameTime);
}
protected override void Draw (GameTime gameTime)
{
graphics.GraphicsDevice.Clear (Color.Blue);
spriteBatch.Begin (SpriteSortMode.BackToFront, BlendState.AlphaBlend, null, null, null, null, Camera.View);
spriteBatch.Draw (tool, new Vector2 (toolPos.X, toolPos.Y), null, Color.White, RotationAngle, originOfToolTexture, 1, SpriteEffects.None, 1);
spriteBatch.End ();
base.Draw (gameTime);
}
You need to convert your cursor position into world position:
Vector2 adjustedPosition = Vector2.Transform(touchCollection[0].Position, Matrix.Invert(camera.View));
Then your direction would be:
direction = adjustedPosition - toolPos;
Edit: Link to Question on GameDev SE: https://gamedev.stackexchange.com/questions/51656/implementing-water-effects-splashes-into-xna-4-0-game
I am creating a 2D XNA game and came across a tutorial on adding water effects (splashes) to an XNA game but after implementing it into my game I can't get it to scale down. Currently it takes up the entire screen.
The Water class looks like this
class Water
{
struct WaterColumn
{
public float TargetHeight;
public float Height;
public float Speed;
public void Update(float dampening, float tension)
{
float x = TargetHeight - Height;
Speed += tension * x - Speed * dampening;
Height += Speed;
}
}
PrimitiveBatch pb;
WaterColumn[] columns = new WaterColumn[201];
static Random rand = new Random();
public float Tension = 0.025f;
public float Dampening = 0.025f;
public float Spread = 0.25f;
RenderTarget2D metaballTarget, particlesTarget;
SpriteBatch spriteBatch;
AlphaTestEffect alphaTest;
Texture2D particleTexture;
private float Scale { get { return spriteBatch.GraphicsDevice.Viewport.Width / (columns.Length - 1f); } }
List<Particle> particles = new List<Particle>();
class Particle
{
public Vector2 Position;
public Vector2 Velocity;
public float Orientation;
public Particle(Vector2 position, Vector2 velocity, float orientation)
{
Position = position;
Velocity = velocity;
Orientation = orientation;
}
}
public Water(GraphicsDevice device, Texture2D particleTexture)
{
pb = new PrimitiveBatch(device);
this.particleTexture = particleTexture;
spriteBatch = new SpriteBatch(device);
metaballTarget = new RenderTarget2D(device, device.Viewport.Width, device.Viewport.Height);
particlesTarget = new RenderTarget2D(device, device.Viewport.Width, device.Viewport.Height);
alphaTest = new AlphaTestEffect(device);
alphaTest.ReferenceAlpha = 175;
var view = device.Viewport;
alphaTest.Projection = Matrix.CreateTranslation(-0.5f, -0.5f, 0) *
Matrix.CreateOrthographicOffCenter(0, view.Width, view.Height, 0, 0, 1);
for (int i = 0; i < columns.Length; i++)
{
columns[i] = new WaterColumn()
{
Height = 240,
TargetHeight = 240,
Speed = 0
};
}
}
// Returns the height of the water at a given x coordinate.
public float GetHeight(float x)
{
if (x < 0 || x > 800)
return 240;
return columns[(int)(x / Scale)].Height;
}
void UpdateParticle(Particle particle)
{
const float Gravity = 0.3f;
particle.Velocity.Y += Gravity;
particle.Position += particle.Velocity;
particle.Orientation = GetAngle(particle.Velocity);
}
public void Splash(float xPosition, float speed)
{
int index = (int)MathHelper.Clamp(xPosition / Scale, 0, columns.Length - 1);
for (int i = Math.Max(0, index - 0); i < Math.Min(columns.Length - 1, index + 1); i++)
columns[index].Speed = speed;
CreateSplashParticles(xPosition, speed);
}
private void CreateSplashParticles(float xPosition, float speed)
{
float y = GetHeight(xPosition);
if (speed > 120)
{
for (int i = 0; i < speed / 8; i++)
{
Vector2 pos = new Vector2(xPosition, y) + GetRandomVector2(40);
Vector2 vel = FromPolar(MathHelper.ToRadians(GetRandomFloat(-150, -30)), GetRandomFloat(0, 0.5f * (float)Math.Sqrt(speed)));
CreateParticle(pos, vel);
}
}
}
private void CreateParticle(Vector2 pos, Vector2 velocity)
{
particles.Add(new Particle(pos, velocity, 0));
}
private Vector2 FromPolar(float angle, float magnitude)
{
return magnitude * new Vector2((float)Math.Cos(angle), (float)Math.Sin(angle));
}
private float GetRandomFloat(float min, float max)
{
return (float)rand.NextDouble() * (max - min) + min;
}
private Vector2 GetRandomVector2(float maxLength)
{
return FromPolar(GetRandomFloat(-MathHelper.Pi, MathHelper.Pi), GetRandomFloat(0, maxLength));
}
private float GetAngle(Vector2 vector)
{
return (float)Math.Atan2(vector.Y, vector.X);
}
public void Update()
{
for (int i = 0; i < columns.Length; i++)
columns[i].Update(Dampening, Tension);
float[] lDeltas = new float[columns.Length];
float[] rDeltas = new float[columns.Length];
// do some passes where columns pull on their neighbours
for (int j = 0; j < 8; j++)
{
for (int i = 0; i < columns.Length; i++)
{
if (i > 0)
{
lDeltas[i] = Spread * (columns[i].Height - columns[i - 1].Height);
columns[i - 1].Speed += lDeltas[i];
}
if (i < columns.Length - 1)
{
rDeltas[i] = Spread * (columns[i].Height - columns[i + 1].Height);
columns[i + 1].Speed += rDeltas[i];
}
}
for (int i = 0; i < columns.Length; i++)
{
if (i > 0)
columns[i - 1].Height += lDeltas[i];
if (i < columns.Length - 1)
columns[i + 1].Height += rDeltas[i];
}
}
foreach (var particle in particles)
UpdateParticle(particle);
particles = particles.Where(x => x.Position.X >= 0 && x.Position.X <= 800 && x.Position.Y - 5 <= GetHeight(x.Position.X)).ToList();
}
public void DrawToRenderTargets()
{
GraphicsDevice device = spriteBatch.GraphicsDevice;
device.SetRenderTarget(metaballTarget);
device.Clear(Color.Transparent);
// draw particles to the metaball render target
spriteBatch.Begin(0, BlendState.Additive);
foreach (var particle in particles)
{
Vector2 origin = new Vector2(particleTexture.Width, particleTexture.Height) / 2f;
spriteBatch.Draw(particleTexture, particle.Position, null, Color.White, particle.Orientation, origin, 2f, 0, 0);
}
spriteBatch.End();
// draw a gradient above the water so the metaballs will fuse with the water's surface.
pb.Begin(PrimitiveType.TriangleList);
const float thickness = 20;
float scale = Scale;
for (int i = 1; i < columns.Length; i++)
{
Vector2 p1 = new Vector2((i - 1) * scale, columns[i - 1].Height);
Vector2 p2 = new Vector2(i * scale, columns[i].Height);
Vector2 p3 = new Vector2(p1.X, p1.Y - thickness);
Vector2 p4 = new Vector2(p2.X, p2.Y - thickness);
pb.AddVertex(p2, Color.White);
pb.AddVertex(p1, Color.White);
pb.AddVertex(p3, Color.Transparent);
pb.AddVertex(p3, Color.Transparent);
pb.AddVertex(p4, Color.Transparent);
pb.AddVertex(p2, Color.White);
}
pb.End();
// save the results in another render target (in particlesTarget)
device.SetRenderTarget(particlesTarget);
device.Clear(Color.Transparent);
spriteBatch.Begin(0, null, null, null, null, alphaTest);
spriteBatch.Draw(metaballTarget, Vector2.Zero, Color.White);
spriteBatch.End();
// switch back to drawing to the backbuffer.
device.SetRenderTarget(null);
}
public void Draw()
{
Color lightBlue = new Color(0.2f, 0.5f, 1f);
// draw the particles 3 times to create a bevelling effect
spriteBatch.Begin();
spriteBatch.Draw(particlesTarget, -Vector2.One, new Color(0.8f, 0.8f, 1f));
spriteBatch.Draw(particlesTarget, Vector2.One, new Color(0f, 0f, 0.2f));
spriteBatch.Draw(particlesTarget, Vector2.Zero, lightBlue);
spriteBatch.End();
// draw the waves
pb.Begin(PrimitiveType.TriangleList);
Color midnightBlue = new Color(0, 15, 40) * 0.9f;
lightBlue *= 0.8f;
float bottom = spriteBatch.GraphicsDevice.Viewport.Height;
float scale = Scale;
for (int i = 1; i < columns.Length; i++)
{
Vector2 p1 = new Vector2((i - 1) * scale, columns[i - 1].Height);
Vector2 p2 = new Vector2(i * scale, columns[i].Height);
Vector2 p3 = new Vector2(p2.X, bottom);
Vector2 p4 = new Vector2(p1.X, bottom);
pb.AddVertex(p1, lightBlue);
pb.AddVertex(p2, lightBlue);
pb.AddVertex(p3, midnightBlue);
pb.AddVertex(p1, lightBlue);
pb.AddVertex(p3, midnightBlue);
pb.AddVertex(p4, midnightBlue);
}
pb.End();
}
}
Then in the Game1.cs I have the following
LoadContent method
protected override void LoadContent()
{
// Create a new SpriteBatch, which can be used to draw textures.
spriteBatch = new SpriteBatch(GraphicsDevice);
font = Content.Load<SpriteFont>("Font");
particleImage = Content.Load<Texture2D>("metaparticle");
backgroundImage = Content.Load<Texture2D>("sky");
rockImage = Content.Load<Texture2D>("rock");
water = new Water(GraphicsDevice, particleImage);
.
.
.
}
In my update method I have the following (along with other code for the game, i am just showing water part)
protected override void Update(GameTime gameTime)
{
lastKeyState = keyState;
keyState = Keyboard.GetState();
lastMouseState = mouseState;
mouseState = Mouse.GetState();
water.Update();
Vector2 mousePos = new Vector2(mouseState.X, mouseState.Y);
// if the user clicked down, create a rock.
if (lastMouseState.LeftButton == ButtonState.Released && mouseState.LeftButton == ButtonState.Pressed)
{
rock = new Rock
{
Position = mousePos,
Velocity = (mousePos - new Vector2(lastMouseState.X, lastMouseState.Y)) / 5f
};
}
// update the rock if it exists
if (rock != null)
{
if (rock.Position.Y < 240 && rock.Position.Y + rock.Velocity.Y >= 240)
water.Splash(rock.Position.X, rock.Velocity.Y * rock.Velocity.Y * 5);
rock.Update(water);
if (rock.Position.Y > GraphicsDevice.Viewport.Height + rockImage.Height)
rock = null;
}
Then in the Draw method I have the following (when the active enum is InGame)
case ActiveScreen.InGame:
water.DrawToRenderTargets();
level.Draw(gameTime, spriteBatch);
DrawHud();
spriteBatch.Begin();
spriteBatch.Draw(backgroundImage, Vector2.Zero, Color.White);
if (rock != null)
rock.Draw(spriteBatch, rockImage);
spriteBatch.End();
water.Draw();
break;
My problem is this obviously takes up the entire screen. I realise why it takes up the entire screen but I can't figure out how to scale it down and set it on a fixed location in the game. If anyone could read through this and direct me towards how I would go about scaling this down successfully, I'd greatly appreciate it.
Notice that
private float Scale { get { return spriteBatch.GraphicsDevice.Viewport.Width / (columns.Length - 1f); } }
creates a Scale corresponding to the entire screen. And in both your Draw() and DrawToRenderTargets() methods
Vector2 p1 = new Vector2((i - 1) * scale, columns[i - 1].Height);
Vector2 p2 = new Vector2(i * scale, columns[i].Height);
which means that the top of the vectors (water columns) will start from the beginning of the screen. That's where you need to make change of.