问题
I have a game, where I want my characters to move in my field square by square, like in Pokémon.
With the code I have, it is moving square by square just it skips on every key hit about 4-5 squares. And just jumps around.
Can someone help me to get my code work in a similar manner like Pokémon?
The code I have is as follows.
public class Map {
private Map() {
Position = new Vector2(0, 0);
}
public string Data { get; set; }
public string[][] MapData { get; set; }
public ContentManager Content { get; set; }
public SpriteBatch SpriteBatch { get; set; }
public Vector2 Position { get; set; }
private Vector2 ArrayPosition;
private readonly Vector2 Speed = new Vector2(40, 32);
public static Map Parse(string path) {
var map = new Map();
var stream = TitleContainer.OpenStream(Path.Combine("Content", path));
using (var sr = new StreamReader(stream)) {
map.Data = sr.ReadToEnd();
}
var lines = map.Data.Split(new string[1] { Environment.NewLine }, StringSplitOptions.None);
var mapHeight = lines.Count();
map.MapData = new string[mapHeight][];
for (int i = 0; i < lines.Count(); i++) {
var elements = lines[i].Split(';');
map.MapData[i] = elements;
}
return map;
}
public void DrawMap(SpriteBatch spriteBatch, ContentManager content, GameTime gametime) {
this.SpriteBatch = spriteBatch;
this.Content = content;
for (int y = 0; y < MapData.Count(); y++) {
var current = MapData[y];
for (int x = 0; x < current.Count(); x++) {
switch (current[x]) {
case "e":
drawEnemy(x, y);
break;
case "P":
case ".":
drawTile(x, y);
break;
case "w":
drawWall(x, y);
break;
}
}
}
drawPlayer();
}
public void Move(Direction pdirection, GameTime gametime) {
var direction = Vector2.Zero;
var x = this.ArrayPosition.X;
var y = this.ArrayPosition.Y;
switch (pdirection) {
case Direction.Up:
if (y > 0 && y < 16) {
direction = new Vector2(0, -1);
}
break;
case Direction.Down:
if (y < 16 && y >= 0) {
direction = new Vector2(0, 1);
}
break;
case Direction.Left:
if (x > 0 && x < 16) {
direction = new Vector2(-1, 0);
}
break;
case Direction.Right:
if (x < 16 && x >= 0) {
direction = new Vector2(1, 0);
}
break;
}
Position += direction * Speed;
}
private void drawPlayer() {
var tile = Position / Speed;
var x = tile.X;
var y = tile.Y;
drawTile((int)x, (int)y);
var texture = Content.Load<Texture2D>("Sprites/player");
this.SpriteBatch.Draw(texture, Position, Color.White);
}
private void drawEnemy(int x, int y) {
drawTile(x, y);
drawTexture(Content.Load<Texture2D>("Sprites/enemy"), x, y);
}
private void drawTile(int x, int y) {
drawTexture(Content.Load<Texture2D>("Tiles/grass"), x, y);
}
private void drawWall(int x, int y) {
drawTexture(Content.Load<Texture2D>("Tiles/wall"), x, y);
}
private void drawTexture(Texture2D texture, int x, int y) {
var rectangle = new Rectangle(x * 40, y * 32, 40, 32);
this.SpriteBatch.Draw(texture, rectangle, Color.White);
}
}
回答1:
It looks to me that you are using a mix of co-ordinate systems to represent things on your map in addition to possible cumulative movement delta error. Consider having one co-ordinate system. I'm not sure how big your map is so lets assume it's 10x10. Therefore tiles; enemys; and the player's co-ordinates should be within {0...9}x{0...9}.
e.g. In your DrawMap()
I see your tiles use a co-ordinate system of 0 to the dimensions of your string[][] MapData
. You leave scaling the co-ordinates until your drawTexture()
as below which I think is a good idea:
private void drawTexture(Texture2D texture, int x, int y) {
var rectangle = new Rectangle(x * 40, y * 32, 40, 32);
this.SpriteBatch.Draw(texture, rectangle, Color.White);
}
Tip: Try to avoid having magic numbers in your code. Consider either defining 40 and 32 as constants or even better determine the tile size at runtime during LoadContent()
.
However, your method Move()
, which moves the player about based on direction
is scaled by Speed
defined below as:
private readonly Vector2 Speed = new Vector2(40, 32);
...which I don't think is a good idea because you are scaling the size of tiles with the player's speed which results in the player's co-ordinates being in an entirely different co-ordinate system than enemy's, walls and other map data. Because it is scaled, you need to divide by the tile size all the time to normalise the player's co-ordinates whenever you wish to interact with the map.
Later when you draw the player you have this unusual line:
private void drawPlayer() {
var tile = Position / Speed;
If p
is position, v
is velocity; t
is time; then p = vt
which follows that t = p / v
so just reading the above line on its own is a little odd. But I know what you are trying to do - determine the co-ordinates of the tile based on tile size.
However if you just normalise the player's position to always be with the range {0...MapData dimensions} without scaling by (40, 32) it would make things simpler.
Changes
In your Move()
method change this line:
Position += direction * Speed;
...to:
Position += direction;
Then change drawPlayer()
from:
private void drawPlayer() {
var tile = Position / Speed;
var x = tile.X;
var y = tile.Y;
drawTile((int)x, (int)y);
var texture = Content.Load<Texture2D>("Sprites/player");
this.SpriteBatch.Draw(texture, Position, Color.White);
}
...to:
private void drawPlayer() {
drawTile((int)Position.x, (int)Position.y);
var texture = Content.Load<Texture2D>("Sprites/player");
var p = Position * new Vector2 (40,32);
this.SpriteBatch.Draw(texture, p, Color.White);
}
Performance tip
- Move all the
Content.Load<>
from yourDrawxxx(...)
toLoadContent(...)
. Generally you want to minimize the loading of resources (particularly the same resources) during your game loop unless your game is streaming (which you are not in this case).
来源:https://stackoverflow.com/questions/29808334/move-sprite-like-in-pok%c3%a9mon