I'm creating a game where the player is drawn in the middle of the screen. In order to modify the direction that the player is looking towards, I'm using the following lines of code:
In the player class, in the Draw() function:
//Draw player
public void Draw(SpriteBatch spriteBatch)
{
spriteBatch.Draw(currentTexture, position, null, Color.White, rotation, centre, 1.0f, SpriteEffects.None, 0f);
}
I'm calling that function in the main class, inside the Draw() function:
//Draw
protected override void Draw(GameTime gameTime)
{
GraphicsDevice.Clear(Color.Black);
spriteBatch.Begin();
//Draw player
player.Draw(spriteBatch);
spriteBatch.End();
base.Draw(gameTime);
}
The player is not being drawn, and no error is showing up.
The weird thing is that I've used this exact same code for drawing in a game I made two days ago and it works fine.
If i use the following lines instead, the player is drawn, but i'm left without the ability to modify player rotation unless I use different textures for each direction:
spriteBatch.Draw(currentTexture, position, Color.White);
I'm guessing that the problem is with were you set the origin. Your centre vector. Try using Vector2.Zero instead of centre and see what happens.
For your center vector you probably want the center of the texture. The Vector2 center paramenter in SpriteBatch.Draw is relative to the texture coordinates. This means that to rotate around the center of your object you would need to have a value of
new Vector2(texture.Width/2, texture.Height/2)
I would recommend creating a method in your player class (Or GameObject class if you're doing that sort of thing) that returns your center with a texture coordinate relative Vector2 (like the one I provided above)! Happy Coding!
Related
I am new to unity and working on a game where a rocket flies up and down through a side-scrolling maze. Simple. The issue is the following code is rotating my rocket on the bottom rather than the center. This is similar to if you were to rotate on "pivot" mode in unity rather than on "center".
private void Rotate(){
float rotationMultiple = Time.deltaTime * rotationSpeed;
rigidBody.freezeRotation = true;
if (Input.GetKey(KeyCode.LeftArrow)){
transform.Rotate(Vector3.forward * rotationMultiple);
}
else if(Input.GetKey(KeyCode.RightArrow)){
transform.Rotate(Vector3.back * rotationMultiple);
}
rigidBody.freezeRotation = false;
}
Your help means a lot as I try to get the hang of things.
In order to rotate around the center of the object, use Renderer.bounds and Bounds.center to get the center point and Transform.RotateAround to rotate the transform around the center point.
You're currently using euler angles to rotate the transform, but since your euler angles vectors only have one nonzero component, we can use them as axis parameters for RotateAround if you keep the axis and rotationMultiple separate:
private rend;
Start()
{
rend = GetComponent<Renderer>();
}
private void Rotate(){
Vector3 centerPoint = rend.bounds.center;
float rotationMultiple = Time.deltaTime * rotationSpeed;
rigidBody.freezeRotation = true;
if (Input.GetKey(KeyCode.LeftArrow)){
transform.RotateAround(centerPoint, Vector3.forward, rotationMultiple);
}
else if(Input.GetKey(KeyCode.RightArrow)){
transform.RotateAround(centerPoint, Vector3.back, rotationMultiple);
}
rigidBody.freezeRotation = false;
}
Be aware that if the shape/size of your renderer changes, the position of the center may change.
From Unity Answers:
When you modify the rotation of a Transform, the rotation happens around the local origin - that is, the center of the local coordinate system for the object. The actual middle of the object may not be placed at the origin - that depends on how it was modeled.
To make the rotation around the actual middle, there are a few things you could do.
Rotate around the bounds center. You can use collider.bounds.center to get the center of the world space bounding box of the collider of the game object, get the center of the local space bounding box of the mesh. In both cases, you can then use RotateAround to rotate around that point. Note that for the mesh bounds, you need to transform the center into global coordinates first.
Make an empty parent game object to the game object you want to rotate. Place the game object such that its middle is placed right at the origin of the parent. You can then rotate the parent game object instead using the normal ways to set the rotation of the transform.
(From https://answers.unity.com/questions/8599/how-do-i-rotate-and-object-around-its-center-in-sc.html)
This should help with your issue if I am understanding it correctly.
This was a great learning experience for me while playing around in the Unity editor. First, the “center” vs “pivot” example I gave shows just how new I am. I figure pivot was always the bottom, turns out that’s just where it was pivoting in this case.
Ultimately the fix I was looking for was Gameobject > center on children. This set the empty rocket wrapper on the center of its children and fixed the weird rotation I was getting.
Thanks all
What I would like to do is to be able to draw a specific set of sprites within a spriteBatch with additive blending. The problem is that the draw order that they're drawn in needs to be preserved and I can't draw everything else in the SpriteBatch with additive blending, so I can't just do this:
spriteBatch.Begin(SpriteSortMode.FrontToBack, BlendState.AlphaBlend);
//Draw some stuff here
spriteBatch.End();
spriteBatch.Begin(SpriteSortMode.FrontToBack, BlendState.Additive);
//Draw stuff with additive blending here
spriteBatch.End();
So my solution would be to write a shader to do what I need and just do this:
spriteBatch.Begin(SpriteSortMode.FrontToBack, BlendState.AlphaBlend);
//Draw some stuff here
foreach(EffectPass pass in AdditiveShader.CurrentTechnique.Passes)
{
pass.Apply()
//Draw stuff with additive shader applied here
}
spriteBatch.End()
But pass.Apply() is literally doing nothing. Even if I try just using a BasicEffect and have it rotate a few degrees, it's doing nothing. The only way I can get it to do anything is to call it like this:
spriteBatch.Begin(SpriteSortMode.FrontToBack, BlendState.AlphaBlend,
null, null, null, AdditiveShader);
Then it actually does something to sprites, but that doesn't really help me because I want to only apply it to specific sprites and still retain the draw order.
What am I doing wrong when using pass.Apply()? Is there a way to draw a set of sprites with additive blending and another set with alpha blending and still keep the draw order? Any help would be greatly appreciated. Thanks.
EDIT: For clarification, I'm working in 2D.
OK, so what I found (Thanks to Shawn Hargreaves - http://blogs.msdn.com/b/shawnhar/archive/2010/04/05/spritebatch-and-custom-renderstates-in-xna-game-studio-4-0.aspx) is that I can use GraphicsDevice.BlendState = BlendState.Additive to change the BlendState on the fly.
So what I did was this:
spriteBatch.Begin(SpriteSortMode.Immediate, BlendState.Alpha);
//Draw with alpha blending here
GraphicsDevice.BlendState = BlendState.Additive;
//Draw specific sprites with additive blending here
GraphicsDevice.BlendState = BlendState.Alpha; //Revert the blendstate to Alpha
//Draw more stuff here with alpha blending
spriteBatch.End();
The problem is that the SpriteSortMode for the spriteBatch has to be set to Immediate or GraphicsDevice.BlendState = BlendState.Additive will do nothing.
So I suppose I'll have to use a custom DepthStencilState to replicate the functionality of SpriteSortMode.FrontToBack.
Inside my project, I have a sprite being draw of a box. I have the camera zoom out when clicking a key. When I zoom out, I want my box to scale it's dimensions so it stays consistent even though the camera has zoomed out and "shrunk" it.
I have tried multiplying the object's dimensions by 10% which seems to be the viewpoint's adjustment when zooming out, but that doesn't seem to work. Now this may sound dumb, but would scaling the sprite in the draw function also change the sprite's dimensions?
Let's say the box is 64x64 pixels. I zoom out 10% and scale the sprite. Does the sprite still have the boundaries as 64x64 or is the up-scaling also changing it's dimensions?
Scaling using SpriteBatch.Draw()s scale argument will just draw the sprite smaller/bigger, i.e. a 64x64 one will appear as 7x7 pixels (the outer pixels being alpha blended if enabled). However there are no size properties on the sprite, if you have your own rectangle, position variables for the sprite SpriteBatch.Draw() of course will not change those.
An alternative is draw the sprite in 3D space then everything is scaled when you move your camera, so the sprite will appear smaller though it will still be a 64x64 sprite.
How to draw a sprite in 3D space? Here is a good tutorial http://www.riemers.net/eng/Tutorials/XNA/Csharp/Series2/Point_sprites.php. (You will need to take time to learn about using 3D viewports, camera's etc, see here: http://msdn.microsoft.com/en-us/library/bb197901.aspx)/
To change sprite dimensions you need to change Rectangle parameter for SpriteBatch.Draw. To calculate zoom on rectange:
Rectangle scaledRect = new Rectangle(originalRectangle.X, originalRectangle.Y, (int)(originalRectangle.Width*zoom), (int)(originalRectangle.Height*zoom)); // where zoom default is 1.0f
When drawing use:
spriteBatch.Draw(Texture, scaledRect, Color.White);
Now I'm sorry to assume it, but without knowing why you doing what you doing - I think you doing something wrong.
You should use camera transformation to zoom out/in. It is done like that:
var transform = Matrix.CreateTranslation(new Vector3(-Position.X, -Position.Y, 0))* // camera position
Matrix.CreateRotationZ(_rotation)* // camera rotation, default 0
Matrix.CreateScale(new Vector3(Zoom, Zoom, 1))* // Zoom default 1
Matrix.CreateTranslation(
new Vector3(
Device.Viewport.Width*0.5f,
Device.Viewport.Height*0.5f, 0)); // Device from DeviceManager, center camera to given position
SpriteBatch.Begin( // SpriteBatch variable
SpriteSortMode.BackToFront, // Sprite sort mode - not related
BlendState.NonPremultiplied, // BelndState - not related
null,
null,
null,
null,
transformation); // set camera tranformation
It will change how sprites are displayed inside sprite batch, however - now you also must account for different mouse coordinates (if you using mouse input). To do that you must transform mouse position to transformed world matrix:
// mouse position, your tranformation matrix
public Vector2 ViewToWorld(Vector2 pos, Matrix transform)
{
return Vector2.Transform(pos, Matrix.Invert(transform));
}
I used the code without direct access to test it, so if something will not work - feel free to ask.
This is not answer to your question directly, if you could provide reason why you want re-size sprite when zooming instead of zooming camera - maybe I could better answer your question, also you should fallow markmnl link to understand world transformations and why you seem to need it in this situation.
I am drawing a line in XNA by using a paint picture of one pixel, then fitting that pixel into a rectangle of height 100, with 2, so it appears as a line. This way I can later draw a rectangle.
I also want to rotate the rectangle but the rotation center is always it's upper left corner. How can I change this?
Use this overload of the Draw method. You'll probably want to set the origin parameter to a non-zero vector.
Set the Origin parameter in the Draw() method.
public void Draw (
Texture2D texture,
Vector2 position,
Nullable<Rectangle> sourceRectangle,
Color color,
float rotation,
Vector2 origin,
Vector2 scale,
SpriteEffects effects,
float layerDepth
)
Where:
origin
The sprite origin; the default is (0,0) which represents the upper-left corner.
I am probably going to smack myself in the face once the answer becomes clear, but I'm afraid I can't figure out what to do with my camera in order to effectively mix scrolling 2D and 3D objects.
Currently I'm using a displacement camera, which I implemented when this was still just a 2D project. The camera displaces the draw position of all 2D objects based on where its position is in the world. In case that's not clear, here's the code:
public void DrawSprite(Sprite sprite)
{
Vector2 drawtopLeftPosition = ApplyTransformation(sprite.Position);
//TODO: add culling logic here
sprite.Draw(spriteBatch, drawtopLeftPosition);
}
private Vector2 ApplyTransformation(Vector2 spritetopLeftPosition)
{
return (spritetopLeftPosition - topLeftPosition);
}
Simple enough. This worked effectively until I tried to push 3D into the equation. I have several spheres which I want to display alongside the 2D game objects. I have already figured out how to Z-order everything properly, but I cannot get the spheres to project correctly. They appear and disappear depending on where the camera is and often fly around erratically with even the slightest camera movement. Here are my camera matrices:
viewMatrix = Matrix.CreateLookAt(new Vector3(Position, 1f), new Vector3(Position , 0), Vector3.Up);
projectionMatrix = Matrix.CreatePerspectiveFieldOfView(MathHelper.PiOver4, graphics.Viewport.AspectRatio, 1f, 1000f);
Note that the Vector2 Position is the absolute center of the camera viewport, not the top left or anything like that. I have also tried using OrthographicOffCenter and Orthographic projections. The viewMatrix updates each frame with the same CreateLookAt function based on the current camera position. Here is the Camera method which draws a 3D object:
public void DrawModel(Sprite3D sprite, GraphicsDevice device)
{
device.BlendState = BlendState.Opaque;
device.DepthStencilState = DepthStencilState.Default;
device.SamplerStates[0] = SamplerState.LinearWrap;
sprite.DrawBasic(this, device);
}
My main point of confusion is whether I should displace the 3D objects as well as the 2D. I have tried doing this but have had more success actually glimpsing the 3D objects on screen if I do not.
Here is the Sprite3D section for drawing:
public Matrix GenerateWorld()
{
return Matrix.CreateScale(Scale) * Matrix.CreateTranslation(new Vector3(Position, -ZPosition)) * Matrix.CreateFromYawPitchRoll(Rotation3D.X, Rotation3D.Y, Rotation3D.Z);
}
private Matrix GenerateDEBUGWorld()
{
return Matrix.CreateScale(Scale) * Matrix.CreateTranslation(Vector3.Zero);
}
public void DrawBasic(Camera camera, GraphicsDevice device)
{
Matrix[] transforms = new Matrix[Model.Bones.Count];
Model.CopyAbsoluteBoneTransformsTo(transforms);
foreach (ModelMesh mesh in Model.Meshes)
{
foreach (BasicEffect effect in mesh.Effects)
{
effect.World = (transforms[mesh.ParentBone.Index] * GenerateDEBUGWorld());
effect.View = camera.ViewMatrix;
effect.Projection = camera.ProjectionMatrix;
}
mesh.Draw();
}
}
GenerateDEBUGWorld simply places the sphere at 0,0 so that I always know where it should be.
A few things I've noticed:
If I set the Z position of the camera to a larger number (say, 10), spheres move less erratically but still wrongly
Using OrthographicOffCenter projection displays tiny little spheres at the center of the screen which do not change in position or scale, even if I multiply the Sprite3D's Scale variable by 1000.
So, the main question: should I even use a displacement camera, or is there a better way to mix 2D and 3D effectively (billboarding, perhaps? but I don't want to add unnecessary complexity to this project)? And, if I do use displacement, should I displace the 3D objects as well as the 2D? Finally, what sort of projection should be used?
Update: If I move the camera back to z position of 100 and make the spheres scale up by 100%, they behave somewhat normally-- when the camera moves left they move right, up they move down, etc, as you'd expect for a perspective projection. However they move far too much in relation to the rest of the 2D objects. Is this a matter of scale? I feel like it's very difficult to reconcile the way sprites are shown and the way 3D objects are shown onscreen...
It sounds like what you're looking for is Unproject - this will take a 2D point and "cast it" into 3D space. If all of your objects are in 3D space, your typical Ortho/LookAt camera should work as you'd expect...I'm also going completely from memory here, since I can't be arsed to look it up. ;)
Just a thought, but your Z in your view matrix is being constrained between 1 and 0. Update the Z component of those Vector3's to be the actual Z of the camera and see what that does for you.
<2 cents/>