XNA Snap sprite to a tile map - xna

I'm looking for the best option on how to handle snapping sprites to a tilemap. I'm trying to make a Chu Chu Rocket clone. If you dont know the game. It is a tilebased game where you place arrows on a specfic tile to direct unts around the maps. So I need to snap the sprites to the center of the tile at all times and then detect a collision with either an arrow which takes up a whole tile or a wall or other obstruction. Any ideas on what the based way would be to detect those things since it would require different kinds of collision detection i believe.

The easiest way to snap the sprites to a tile is to draw them centered at a tile. The collision detection can be done in your update function by checking against your level object.
class Mouse
{
public int XTile;
public int YTile;
public int XDelta;
public int YDelta;
}
//in Update
if (Level[mouse.YTile][mouse.XTile] == Tiles.Arrow)
{
//change mouse.XDelta and mouse.YDelta based on the direction of the arrow
}
if (Level[mouse.YTile + YDelta][mouse.XTile + XDelta] == Tiles.Wall)
{
//change mouse.XDelta and mouse.YDelta based on wall rules
}
//in Draw
int tileSize = 32; //or whatever size tile you are using
spriteBatch.Draw(mouseSprite, new Vector2(mouse.XTile * tileSize,
mouse.YTile * tileSize), Color.White);
//or, if the mouseSprite doesn't take up the whole tile
int sizeDifference = tileSize - mouseSprite.Width;
spriteBatch.Draw(mouseSprite, new Vector2(mouse.XTile * tileSize + sizeDifference / 2f,
mouse.YTile * tileSize + sizeDifference / 2f), Color.White);

Related

Fast moving object leaves a "ghost" trail

I'm trying to draw a bullet in Monogame with a high velocity. When I draw it for about 400px/sec "Which is quite slow" but around 1500px/sec it starts "duplicating" or "ghosting" the Texture. I am fairly new to Monogame and do not have alot of knowledge on Graphics.
How can I move an object with High Velocity without creating a "ghost" effect ?
SpriteBatch Begin :
sb.Begin(SpriteSortMode.Deferred, BlendState.AlphaBlend, SamplerState.LinearWrap, DepthStencilState.None, RasterizerState.CullNone,
null, Global.Camera.GetViewTransformationMatrix());
Draw Method :
public override void Draw(SpriteBatch sb)
{
Vector2 origin = new Vector2(source.Width / 2, source.Height / 2);
Rectangle tRect = Bounds;
sb.Draw(
texture: TDTGame.GameAssets.Texture,
destinationRectangle: tRect,
sourceRectangle: source,
rotation: MathHelper.ToRadians(Rotation - 270f), //Set rotation to forward of the texture.
color: Color.White,
origin: origin,
layerDepth: 1f
);
}
Edit:
Youtube Link : here
Movement of the bullet :
float traveledDistance;
public override void Update(GameTime gt)
{
float deltaTime = (float)gt.ElapsedGameTime.TotalSeconds;
traveledDistance += Speed * deltaTime;
Position += Forward * Speed * deltaTime;
if (traveledDistance > Range)
{
Destroy();
}
}
This is likely an artifact of low frame rate. The higher the frame rate, the less your brain will register the fact that the bullet's "movement" is simply drawing the same image in multiple and changing locations over time :)
As stated the traces are probably in your eyes, not on the screen. If you want to overcome this effect, you may want to skip some frames (maybe completely remove the bullet from screen or at least skip movement).

How can I calculate point of UV texture pressed on object?

How can I calculate point of UV texture pressed on object?
For example: I have a ball textured by Earth uv map and I pressed any city and I'd like to get a possition that city on Earth bitmp?
I'm going to try explain :)
I have a code:
bool draw;
int old_position_X;
int old_position_Y;
void __fastcall TForm1::Image3D(TObject *Sender, TShiftState Shift, float X,
float Y, TVector3D &RayPos, TVector3D &RayDir)
{
if (Shift.Contains(ssLeft))
{
if (draw==true)
{
TVector3D HitPos;
Image3D->Context->Pick(X, Y, TProjection::pjCamera, RayPos, RayDir);
RayCastPlaneIntersect(RayPos, RayDir, Image3D->AbsolutePosition, Image3D->AbsoluteDirection, HitPos) ;
HitPos.X -= Image3D->Position->X;
HitPos.Y -= Image3D->Position->Y;
int w=Image3D->Bitmap->Width;
int h=Image3D->Bitmap->Height;
int x=(w/Image3D->Width)*(HitPos.X+Image3D->Width/2.0);
int y=(h/Image3D->Height)*(HitPos.Y+Image3D->Height/2.0);
Image3D->Bitmap->Canvas->BeginScene();
Image3D->Bitmap->Canvas->Stroke->Kind=TBrushKind::bkSolid;
Image3D->Bitmap->Canvas->Stroke->Color=claRed;
Image3D->Bitmap->Canvas->DrawLine(TPointF(old_position_X,old_position_Y),TPointF(x,y),1.0);
Image3D->Bitmap->Canvas->EndScene();
old_position_X=x;
old_position_Y=y;
}
}
else
{
draw=false;
}
}
I can zoom, rotate and move the Image3D and that code make me paint on the Image3D.
By the way I don't understand why I have to divide Image3D width and height by 2 but thats work :) I don't understand dependence between 3D object values (scale, positions etc) and pixels... Especially scale X,Y,Z and Width, Height of 3D objects... And dependence with size of textures and scale of 3D objects...
And now, I'd like to make the same on imported models. How to calculate that position on texture.
I don't expect exactly the code but I would ask for guidance, example code etc
anybody?
The usual way to that is:
Calculate which triangle you have hit, using the ray.
Get the UV coordinates of its three vertices and interpolate.

Calcul new coords of camera after a 90 degres rotation in a isometric 2D projection

I made a 2D isometric renderer. It works fine but now I want to show my scene from 4 differents point of view (NO NW SE SW) but, on a 90° rotation, my camera cannot keep the center of my scene on screen.
What's working :
I calcul new projection of scene to match the new viewport (x y z in my world).
I reorganise part of my scene(chunk) to draw them in a correct order
I reorganise 'tiles' of 'chunks' to draw them in a correct order
I can keep the correct center with a 180 degres rotation.
What's do not working :
I cannot find a correct translation to apply to my camera after a 90 degres rotation.
What I know :
To keep the same center on a 180° rotation with my camera I have to do this :
camera.Position -= new Vector2(2 * camera.Position.X + camera.Width, 2 * camera.Position.Y + camera.Height);
Illustration
If the center of your map is origo (0,0,0), this gets easy:
First you store your default camera position in a Vector3 CameraOffset, then you calculate position using a rotation-matrix. 90* in redians is half a Pi, so we will use PiOverTwo. We will also use an enum to decide what direction to be pointing, so you can say
Camera.Orientation = Orientation.East;
and the camera should fix itself :)
public enum Orientation
{
North, East, South, West
}
in camera:
public Vector3 Position { get; protected set; }
Vector3 _CameraOffset = new Vector3(0, 20, 20);
public Vector3 CameraOffset
{
get
{
return _Orientation;
}
set
{
_Orientation = value;
UpdateOrientation();
}
}
Orientation _Orientation = Orientation.North;
public Orientation Orientation
{
get
{
return _Orientation;
}
set
{
_Orientation = value;
UpdateOrientation();
}
}
private void UpdateOrientation()
{
Position = Vector3.Transform(CameraOffset, Matrix.CreateRotationY(MathHelper.PiOverTwo * (int)Orientation));
}
If you want a gliding movement between positions, I think I can help too ;)
If your camera does not focus on Vector3.Zero and should not rotate around it, you just need to change:
Position = Vector3.Transform(CameraOffset, Matrix.CreateRotationY(MathHelper.PiOverTwo * (int)Orientation));
to:
Position = Vector3.Transform(CameraOffset, Matrix.CreateRotationY(MathHelper.PiOverTwo * (int)Orientation) * Matrix.CreateTranslation(FocusPoint));
Here, FocusPoint is the point in 3D that you rotate around (your worlds center). And now you also know how to let your camera move around, if you call UpdateOrientation() in your Camera.Update() ;)
EDIT; So sorry, totally missed the point that you use 2D. I'll be back later to see if I can help :P

Newly loaded sprites not behaving properly in XNA 4.0

Here is the code for a project im working on, where an enemy moves back and forth at the bottom of the screen.
class enemy1
{
Texture2D texture;
public Vector2 position;
bool isAlive = false;
Random rand;
int whichSide;
public enemy1(Texture2D texture, Vector2 position)
{
this.texture = texture;
this.position = position;
}
public void Update()
{
if (isAlive)
{
if (whichSide == 1)
{
position.X += 4;
if (position.X > 1000 + texture.Width)
isAlive = false;
}
if (whichSide == 2)
{
position.X -= 4;
if (position.X < 0)
isAlive = false;
}
}
else
{
rand = new Random();
whichSide = rand.Next(1, 3);
SetInStartPosition();
}
}
private void SetInStartPosition()
{
isAlive = true;
if (whichSide == 1)
position = new Vector2(0 - texture.Width, 563 - texture.Height);
if (whichSide == 2)
position = new Vector2(1000 + texture.Width, 563 - texture.Height);
}
public void Draw(SpriteBatch spriteBatch)
{
spriteBatch.Draw(texture, position, Color.White);
}
}
Now i want there to be a few enemys going back and forth but they start at differant positions so it looks like there is a few enemys going back and forth at the bottom of the screen. I have managed to draw a few other enemies on the screen, except they do not behave like the first enemy. They just are pictures on a screen not moving anywhere. So now all i have is the hero moving around and one enemy at the bottom of the screen, along with 5 other enemys sitting at the top of the screen doing nothing. How do i easily add a new sprite from a class that has the same behavior, at any time, while not making a billion variables to store them in?
Generally it's a good idea to have similar logic contained within the proper class, so if all Sprites where to do the same thing, then all you would need to do is put your movement code inside a public method and then call that method inside Update().
So, if your Sprite class looks something like this:
public class Sprite
{
private Vector2 Position;
public Sprite(Texture2D texture, Vector2 position)
{
Position = position;
}
//then just add this
public void MoveSprite(int amount)
{
position.X += amount;
}
}
Now, the object name "Sprite" is pretty generic, you will more than likely have many "Sprites" in your game.
So you're going to want to follow good OOP practices and maybe name this specific sprite something different and then have it derive from this class we're looking at right now. (But i'm not going to make design decisions for you)
This was a vague question, but that's my best shot at an answer for you.

XNA isometric tiles rendering issue

I'm currently working on a XNA game prototype. I'm trying to achieve a isometric view of the game world (or is it othographic?? I'm not sure which is the right term for this projection - see pictures).
The world should a tile-based world made of cubic tiles (e.g. similar to Minecraft's world), and I'm trying to render it in 2D by using sprites.
So I have a sprite sheet with the top face of the cube, the front face and the side (visible side) face. I draw the tiles using 3 separate calls to drawSprite, one for the top, one for the side, one for the front, using a source rectangle to pick the face I want to draw and a destination rectangle to set the position on the screen according to a formula to convert from 3D world coordinates to isometric (orthographic?).
(sample sprite:
)
This works good as long as I draw the faces, but if I try to draw fine edges of each block (as per a tile grid) I can see that I get a random rendering pattern in which some lines are overwritten by the face itself and some are not.
Please note that for my world representation, X is left to right, Y is inside screen to outside screen, and Z is up to down.
In this example I'm working only with top face-edges. Here is what I get (picture):
I don't understand why some of the lines are shown and some are not.
The rendering code I use is (note in this example I'm only drawing the topmost layers in each dimension):
/// <summary>
/// Draws the world
/// </summary>
/// <param name="spriteBatch"></param>
public void draw(SpriteBatch spriteBatch)
{
Texture2D tex = null;
// DRAW TILES
for (int z = numBlocks - 1; z >= 0; z--)
{
for (int y = 0; y < numBlocks; y++)
{
for (int x = numBlocks - 1; x >=0 ; x--)
{
myTextures.TryGetValue(myBlockManager.getBlockAt(x, y, z), out tex);
if (tex != null)
{
// TOP FACE
if (z == 0)
{
drawTop(spriteBatch, x, y, z, tex);
drawTop(spriteBatch, x, y, z, outlineTexture);
}
// FRONT FACE
if(y == numBlocks -1)
drawFront(spriteBatch, x, y, z, tex);
// SIDE FACE
if(x == 0)
drawSide(spriteBatch, x, y, z, tex);
}
}
}
}
}
private void drawTop(SpriteBatch spriteBatch, int x, int y, int z, Texture2D tex)
{
int pX = OffsetX + (int)(x * TEXTURE_TOP_X_OFFRIGHT + y * TEXTURE_SIDE_X);
int pY = OffsetY + (int)(y * TEXTURE_TOP_Y + z * TEXTURE_FRONT_Y);
topDestRect.X = pX;
topDestRect.Y = pY;
spriteBatch.Draw(tex, topDestRect, TEXTURE_TOP_RECT, Color.White);
}
I tried using a different approach, creating a second 3-tiers nested for loop after the first one, so I keep the top face drawing in the first loop and the edge highlight in the second loop (I know, this is inefficient, I should also probably avoid having a method call for each tile to draw it, but I'm just trying to get it working for now).
The results are somehow better but still not working as expected, top rows are missing, see picture:
Any idea of why I'm having this problem? In the first approach it might be a sort of z-fighting, but I'm drawing sprites in a precise order so shouldn't they overwrite what's already there?
Thanks everyone
Whoa, sorry guys I'm an idiot :) I started the batch with SpriteBatch.begin(SpriteSortMode.BackToFront) but I didn't use any z-value in the draw.
I should have used SpriteSortMode.Deferred! It's now working fine. Thanks everyone!
Try tweaking the sizes of your source and destination rectangles by 1 or 2 pixels. I have a sneaking suspicion this has something to do with the way these rectangles are handled as sort of 'outlines' of the area to be rendered and a sort of off-by-one problem. This is not expert advice, just a fellow coder's intuition.
Looks like a sub pixel precision or scaling issue. Also try to ensure your texture/tile width/height is a power of 2 (32, 64, 128, etc.) as that could make the effect less bad as well. It's really hard to tell just from those pictures.
I don't know how/if you scale everything, but you should try to avoid rounding wherever possible (especially inside your drawTop() method). Every time you round some position/coordinate chances are good you might increase the error/random offsets. Try to use double (or better: float) coordinates instead of integer.

Resources