我遇到的问题是围绕如何使用名为Test Map.png
的单个png包围我的大脑:
屏幕周围有黑色边框,玩家可以使用较小的踩踏块来测试碰撞。我通过使用一个玩家类和主类Game1.cs
来引力和更新游戏。我用这个球:
这是我在屏幕上移动的精灵。
以下是player.cs
:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
namespace Gravity_Test_V2
{
class Player
{
public Texture2D Texture;
public Vector2 Velocity;
public Vector2 Position;
public float ground;
private float Speed;
private Rectangle screenBound;
public bool isJumping; //are we jumping or not
public bool goingUp; //if going up or not
public float initialVelocity; //initial velocity
private float jumpU; //How high the player can jump
private float g; //gravity
public float t; //time
private KeyboardState prevKB;
public Player(Texture2D Texture, Vector2 Position, float Speed, Rectangle screenBound)
{
this.Texture = Texture;
this.Position = Position;
ground = Position.Y;
this.Speed = Speed;
this.screenBound = screenBound;
Velocity = Vector2.Zero;
isJumping = goingUp = true;
jumpU = 2.5f;
g = -9.8f;
t = 0;
}
public void Update(GameTime gameTime)
{
Position.X += (Velocity.X * Speed);
//Set the Y position to be subtracted so that the upward movement would be done by decreasing the Y value
Position.Y -= (Velocity.Y * Speed);
goingUp = (Velocity.Y > 0);
// TODO: Add your update logic here
if (isJumping == true)
{
//motion equation using velocity: v = u + at
Velocity.Y = (float)(initialVelocity + (g * t));
//Increase the timer
t += (float)gameTime.ElapsedGameTime.TotalSeconds;
}
if (isJumping == true && Position.Y > screenBound.Height - Texture.Height)
{
Position.Y = ground = screenBound.Height - Texture.Height;
Velocity.Y = 0;
isJumping = false;
t = 0;
}
if (Position.X < 0)
{
//if Texture touches left side of the screen, set the position to zero and the velocity to zero.
Position.X = 0;
Velocity.X = 0;
}
else if (Position.X + Texture.Width > screenBound.Width)
{
//if Texture touches left side of the screen, set the position to zero and the velocity to zero.
Position.X = screenBound.Width - Texture.Width;
Velocity.X = 0;
}
if (Position.Y < 0)
{
//if the Texture touches the top of the screen, reset the timer and set the initial velocity to zero.
Position.Y = 0;
t = 0;
initialVelocity = 0;
}
}
public void Input(KeyboardState keyState)
{
if (keyState.IsKeyDown(Keys.Space) && (isJumping == false || Position.Y == ground))
{
isJumping = true;
initialVelocity = jumpU;
}
if (keyState.IsKeyDown(Keys.Left) && !keyState.IsKeyDown(Keys.Right))
{
if (Velocity.X > -1.0f)
{
Velocity.X -= (1.0f / 10);
}
else
{
Velocity.X = -1.0f;
}
}
else if (!keyState.IsKeyDown(Keys.Left) && keyState.IsKeyDown(Keys.Right))
{
if (Velocity.X < 1.0f)
{
Velocity.X += (1.0f / 10);
}
else
{
Velocity.X = 1.0f;
}
}
else
{
if (Velocity.X > 0.05 || Velocity.X < -0.05)
Velocity.X *= 0.70f;
else
Velocity.X = 0;
}
prevKB = keyState;
}
public void Fall()
{
t = 0;
initialVelocity = 0;
}
public void Draw(SpriteBatch spriteBatch)
{
spriteBatch.Draw(Texture, new Rectangle((int)Position.X, (int)Position.Y, Texture.Width, Texture.Height), Color.White);
}
}
}
是否有一些简单的方法可以让玩家在测试图的纹理内部与Test Map.png
发生碰撞?
编辑:2014年1月21日上午9点'ish'
第一级:
编辑:2014年1月21日10:27
我使用了一个基于pixle的系统来测试玩家是否与一个对象相关,但我试图将该对象从游戏中分解为类,它将停止工作。我将我的动作和碰撞项目混合在一起,试着让它发挥作用。我拿player.cs
(我没有改变)并将基于像素的碰撞添加到Game1.cs
我需要知道如何制作播放器,女巫正由player.cs
类控制,被Game1.cs
类看到并被player.cs
类调用时使用。
注意*我也改变了它,以便游戏使用基于像素的系统提供的下降三角形。当我能够完成这项工作时,我将添加测试图像。
目前玩家可以移动和跳跃,但不会被重新考虑为碰撞。
编辑:2014年1月21日10:34
我使用了2个项目:
碰撞: http://xbox.create.msdn.com/en-US/education/catalog/tutorial/collision_2d_perpixel
动态系统: http://gamepopper.co.uk/academic-projects/2012-2/jumping-platformer-example/
我把它们混合在一起并使用它们来制作我自己的平台。
Game1.cs
:
using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.GamerServices;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Media;
namespace Collision_Test
{
public class Game1 : Microsoft.Xna.Framework.Game
{
GraphicsDeviceManager graphics;
SpriteBatch spriteBatch;
KeyboardState prevKB;
Player player;
SpriteFont font;
Texture2D personTexture;
Texture2D blockTexture;
// The color data for the images; used for per pixel collision
Color[] personTextureData;
Color[] blockTextureData;
Vector2 personPosition;
const int PersonMoveSpeed = 5;
public static int screenWidth = 800;
public static int screenHeight = 500;
// Blocks
List<Vector2> blockPositions = new List<Vector2>();
float BlockSpawnProbability = 0.01f;
const int BlockFallSpeed = 1;
Random random = new Random();
// For when a collision is detected
bool personHit = false;
// The sub-rectangle of the drawable area which should be visible on all TVs
Rectangle safeBounds;
// Percentage of the screen on every side is the safe area
const float SafeAreaPortion = 0.05f;
public Game1()
{
graphics = new GraphicsDeviceManager(this);
Content.RootDirectory = "Content";
this.graphics.PreferredBackBufferWidth = screenWidth;
this.graphics.PreferredBackBufferHeight = screenHeight;
this.graphics.ApplyChanges();
}
protected override void Initialize()
{
base.Initialize();
// Calculate safe bounds based on current resolution
Viewport viewport = graphics.GraphicsDevice.Viewport;
safeBounds = new Rectangle(
(int)(viewport.Width * SafeAreaPortion),
(int)(viewport.Height * SafeAreaPortion),
(int)(viewport.Width * (1 - 2 * SafeAreaPortion)),
(int)(viewport.Height * (1 - 2 * SafeAreaPortion)));
// Start the player in the center along the bottom of the screen
personPosition.X = (safeBounds.Width - personTexture.Width) / 2;
personPosition.Y = safeBounds.Height - personTexture.Height;
}
/// <summary>
/// Load your graphics content.
/// </summary>
protected override void LoadContent()
{
blockTexture = Content.Load<Texture2D>("Block");
personTexture = Content.Load<Texture2D>("Person");
font = Content.Load<SpriteFont>("Font");
player = new Player(personTexture, Vector2.Zero, 6.0f, new Rectangle(0, 0,
this.graphics.PreferredBackBufferWidth,
this.graphics.PreferredBackBufferHeight));
// Extract collision data
blockTextureData =
new Color[blockTexture.Width * blockTexture.Height];
blockTexture.GetData(blockTextureData);
personTextureData =
new Color[personTexture.Width * personTexture.Height];
personTexture.GetData(personTextureData);
// Create a sprite batch to draw those textures
spriteBatch = new SpriteBatch(graphics.GraphicsDevice);
}
void HandleInput(KeyboardState keyState)
{
player.Input(keyState);
if (prevKB.IsKeyUp(Keys.F) && keyState.IsKeyDown(Keys.F))
{
this.graphics.ToggleFullScreen();
this.graphics.ApplyChanges();
}
}
protected override void Update(GameTime gameTime)
{
// Get input
KeyboardState keyboard = Keyboard.GetState();
GamePadState gamePad = GamePad.GetState(PlayerIndex.One);
HandleInput(Keyboard.GetState());
player.Update(gameTime);
prevKB = Keyboard.GetState();
// Allows the game to exit
if (gamePad.Buttons.Back == ButtonState.Pressed ||
keyboard.IsKeyDown(Keys.Escape))
{
this.Exit();
}
// Spawn new falling blocks
if (random.NextDouble() < BlockSpawnProbability)
{
float x = (float)random.NextDouble() *
(Window.ClientBounds.Width - blockTexture.Width);
blockPositions.Add(new Vector2(x, -blockTexture.Height));
}
// Get the bounding rectangle of the person
Rectangle personRectangle =
new Rectangle((int)personPosition.X, (int)personPosition.Y,
personTexture.Width, personTexture.Height);
// Update each block
personHit = false;
for (int i = 0; i < blockPositions.Count; i++)
{
// Animate this block falling
blockPositions[i] =
new Vector2(blockPositions[i].X,
blockPositions[i].Y + BlockFallSpeed);
// Get the bounding rectangle of this block
Rectangle blockRectangle =
new Rectangle((int)blockPositions[i].X, (int)blockPositions[i].Y,
blockTexture.Width, blockTexture.Height);
// Check collision with person
if (IntersectPixels(personRectangle, personTextureData,
blockRectangle, blockTextureData))
{
personHit = true;
}
// Remove this block if it have fallen off the screen
if (blockPositions[i].Y > Window.ClientBounds.Height)
{
blockPositions.RemoveAt(i);
// When removing a block, the next block will have the same index
// as the current block. Decrement i to prevent skipping a block.
i--;
}
}
base.Update(gameTime);
}
/// <summary>
/// This is called when the game should draw itself.
/// </summary>
/// <param name="gameTime">Provides a snapshot of timing values.</param>
protected override void Draw(GameTime gameTime)
{
GraphicsDevice device = graphics.GraphicsDevice;
// Change the background to red when the person was hit by a block
if (personHit)
{
device.Clear(Color.Red);
}
else
{
device.Clear(Color.CornflowerBlue);
}
spriteBatch.Begin();
player.Draw(spriteBatch);
// Draw blocks
foreach (Vector2 blockPosition in blockPositions)
spriteBatch.Draw(blockTexture, blockPosition, Color.White);
spriteBatch.End();
base.Draw(gameTime);
}
/// <summary>
/// Determines if there is overlap of the non-transparent pixels
/// between two sprites.
/// </summary>
/// <param name="rectangleA">Bounding rectangle of the first sprite</param>
/// <param name="dataA">Pixel data of the first sprite</param>
/// <param name="rectangleB">Bouding rectangle of the second sprite</param>
/// <param name="dataB">Pixel data of the second sprite</param>
/// <returns>True if non-transparent pixels overlap; false otherwise</returns>
static bool IntersectPixels(Rectangle rectangleA, Color[] dataA,
Rectangle rectangleB, Color[] dataB)
{
// Find the bounds of the rectangle intersection
int top = Math.Max(rectangleA.Top, rectangleB.Top);
int bottom = Math.Min(rectangleA.Bottom, rectangleB.Bottom);
int left = Math.Max(rectangleA.Left, rectangleB.Left);
int right = Math.Min(rectangleA.Right, rectangleB.Right);
// Check every point within the intersection bounds
for (int y = top; y < bottom; y++)
{
for (int x = left; x < right; x++)
{
// Get the color of both pixels at this point
Color colorA = dataA[(x - rectangleA.Left) +
(y - rectangleA.Top) * rectangleA.Width];
Color colorB = dataB[(x - rectangleB.Left) +
(y - rectangleB.Top) * rectangleB.Width];
// If both pixels are not completely transparent,
if (colorA.A != 0 && colorB.A != 0)
{
// then an intersection has been found
return true;
}
}
}
// No intersection found
return false;
}
}
}
答案 0 :(得分:1)
如果纹理的背景是透明的,您可以使用Per-Pixel Collision detection.
基本上它会检查像素而不是矩形框来确定是否发生了碰撞。鉴于你的“球员”是一个球,无论如何使用它可能是一个好主意。
答案 1 :(得分:0)
考虑到你的地图只是黑色和白色,您可以在开始游戏之前处理它并获得每个Rectangle
黑色区域的坐标,然后通过检查球边界框和矩形之间的交叉点来使用它来使用简单的碰撞检测。
例如,在伪代码中:
你有一个清单:
List<Rectangle> rects = new List<Rectangle>();
void LoadMap()
{
Texture2D map = Content.Load<Texture2D>(@"TexturePath");
//Get a 1D array with image's pixels
Color[] map_pixels = new Color[map.Width * map.Height];
map.GetData(map_pixels);
//Convert it in a 2D array
Color[,] map_pixels_2D = new Color[map.Width, map.Height];
for (int x = 0; x < map.Width; x++)
for (int y = 0; y < map.Height; y++)
map_pixels_2D[x, y] = map_pixels[x + y * map.Width];
//**NOTE THAT**: From here it is just an example, probably not working good,
//I wrote it just to share the idea
//Here goes the code to trace rectangles in the map
Rectangle r = Rectangle.Empty;
bool NWvertex_done = false, NEvertex_done = false, SWvertex_done = false;
for (int x = 0; x < map.Width; x++)
{
if (!SWvertex_done)
{
if (map_pixels_2D[x, y+1] == Color.White); //last bottom vertex
{
r.Height = r.Y + y;
SWvertex_done = true;
rects.Add(r);
NWvertex_done = false;
NEvertex_done = false;
r = Rectangle.Empty;
}
}
for (int y = 0; y < map.Height; y++)
{
if (map_pixels_2D[x, y] != Color.White
{
if (!NWvertex_done)
{
SWvertex_done = false;
r.X = x;
r.Y = y;
NWvertex_done = true;
}
else if(!NEvertex_done)
{
if (map_pixels_2D[x, y+1] == Color.White); //last right vertex
{
r.Width = r.X + x;
NEvertex_done = true;
}
}
}
}
}
}
public override void Update(GameTime gametime)
{
//maybe other things
//
foreach (Rectangle rect in rects)
{
//Better with Distance of ball-center and rect
if (ballRect.Intersect(rect))
{
//there is a collision!
//Do something
}
break;
}
//maybe other things
//
}