像在神奇宝贝中一样移动精灵
Move sprite like in Pokémon
我有一个游戏,我希望我的角色在我的场地中一个方格一个方格地移动,就像在 Pokémon 中一样。
使用我的代码,它会逐格移动,只是在每次按键时跳过大约 4-5 个方格。然后就跳来跳去。
有人可以帮助我让我的代码以类似于 Pokémon 的方式工作吗?
我的代码如下
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);
}
}
在我看来,除了可能的累积运动增量误差外,您还混合使用坐标系来表示地图上的事物。考虑使用 one 坐标系。我不确定您的地图有多大,所以假设它是 10x10。因此瓷砖;敌人;并且玩家的坐标应在 {0...9}x{0...9} 范围内。
例如在您的 DrawMap()
中,我看到您的瓷砖使用的坐标系为 0 到 string[][] MapData
的尺寸。你离开缩放坐标直到你的 drawTexture()
如下我认为是个好主意:
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);
}
提示:尽量避免在代码中使用幻数。考虑将 40 和 32 定义为常量,或者在 LoadContent()
.
期间更好地确定运行时的图块大小
但是,您的方法 Move()
基于 direction
移动玩家,其缩放比例为 Speed
,定义如下:
private readonly Vector2 Speed = new Vector2(40, 32);
...我认为这不是一个好主意,因为您正在根据玩家的速度缩放图块的大小,这导致玩家的坐标与敌人的坐标系完全不同,墙壁和其他地图数据。因为它是按比例缩放的,所以您需要始终除以图块大小,以便在您希望与地图交互时标准化玩家的坐标。
稍后当您绘制玩家时,您会看到这条不寻常的线:
private void drawPlayer() {
var tile = Position / Speed;
如果p
是位置,v
是速度; t
是时间;然后 p = vt
紧随其后 t = p / v
所以单独阅读上面的行有点奇怪。但我知道您要做什么 - 根据图块大小确定图块的坐标。
但是,如果您只是将玩家的位置标准化为始终在 {0...MapData 维度} 范围内而不按 (40, 32) 进行缩放,这会使事情变得更简单。
变化
在您的 Move()
方法中更改此行:
Position += direction * Speed;
...至:
Position += direction;
然后将 drawPlayer()
更改为:
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 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);
}
性能提示
- 将所有
Content.Load<>
从您的 Drawxxx(...)
移动到 LoadContent(...)
。一般来说,您希望在游戏循环期间尽量减少资源加载(尤其是相同的资源),除非您的游戏正在流式传输(在这种情况下您不是)。
我有一个游戏,我希望我的角色在我的场地中一个方格一个方格地移动,就像在 Pokémon 中一样。
使用我的代码,它会逐格移动,只是在每次按键时跳过大约 4-5 个方格。然后就跳来跳去。
有人可以帮助我让我的代码以类似于 Pokémon 的方式工作吗?
我的代码如下
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);
}
}
在我看来,除了可能的累积运动增量误差外,您还混合使用坐标系来表示地图上的事物。考虑使用 one 坐标系。我不确定您的地图有多大,所以假设它是 10x10。因此瓷砖;敌人;并且玩家的坐标应在 {0...9}x{0...9} 范围内。
例如在您的 DrawMap()
中,我看到您的瓷砖使用的坐标系为 0 到 string[][] MapData
的尺寸。你离开缩放坐标直到你的 drawTexture()
如下我认为是个好主意:
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);
}
提示:尽量避免在代码中使用幻数。考虑将 40 和 32 定义为常量,或者在 LoadContent()
.
但是,您的方法 Move()
基于 direction
移动玩家,其缩放比例为 Speed
,定义如下:
private readonly Vector2 Speed = new Vector2(40, 32);
...我认为这不是一个好主意,因为您正在根据玩家的速度缩放图块的大小,这导致玩家的坐标与敌人的坐标系完全不同,墙壁和其他地图数据。因为它是按比例缩放的,所以您需要始终除以图块大小,以便在您希望与地图交互时标准化玩家的坐标。
稍后当您绘制玩家时,您会看到这条不寻常的线:
private void drawPlayer() {
var tile = Position / Speed;
如果p
是位置,v
是速度; t
是时间;然后 p = vt
紧随其后 t = p / v
所以单独阅读上面的行有点奇怪。但我知道您要做什么 - 根据图块大小确定图块的坐标。
但是,如果您只是将玩家的位置标准化为始终在 {0...MapData 维度} 范围内而不按 (40, 32) 进行缩放,这会使事情变得更简单。
变化
在您的 Move()
方法中更改此行:
Position += direction * Speed;
...至:
Position += direction;
然后将 drawPlayer()
更改为:
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 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);
}
性能提示
- 将所有
Content.Load<>
从您的Drawxxx(...)
移动到LoadContent(...)
。一般来说,您希望在游戏循环期间尽量减少资源加载(尤其是相同的资源),除非您的游戏正在流式传输(在这种情况下您不是)。