so I'm currently working on resolution independence for my game, and I'm testing it out on a sword image. The position changing is working, but whenever I start doing the size I end up with a blank screen.
These are the functions I run to get the new position and size of the sprite.
private static float CalcRatio(Vector2 size)
{
return size.Y / size.X;
}
public static Vector2 CalculateNewPos(Vector2 refPos, Vector2 refScreenSize, Vector2 currentScreenSize)
{
return new Vector2((refPos.X / refScreenSize.X) * currentScreenSize.X,
(refPos.Y / refScreenSize.Y) * currentScreenSize.Y);
}
public static Vector2 CalculateNewSize(Vector2 refSize, Vector2 refScreenSize, Vector2 currenScreenSize)
{
float origRatio = CalcRatio(refSize);
float perW = refSize.X * 100f / refScreenSize.X;
float newW = perW / 100f * currenScreenSize.X;
float newH = newW * origRatio;
return new Vector2(newW, newH);
}
In the Initialization function in Game1 I run this code:
graphics.PreferredBackBufferHeight = GraphicsAdapter.DefaultAdapter.CurrentDisplayMode.Height;
graphics.PreferredBackBufferWidth = GraphicsAdapter.DefaultAdapter.CurrentDisplayMode.Width;
swordPosition = CalculateNewPos(swordRefPosition, new Vector2(1920, 1080), new Vector2(GraphicsAdapter.DefaultAdapter.CurrentDisplayMode.Width, GraphicsAdapter.DefaultAdapter.CurrentDisplayMode.Height));
swordSize = CalculateNewSize(swordRefSize, new Vector2(1920, 1080), new Vector2(GraphicsAdapter.DefaultAdapter.CurrentDisplayMode.Width, GraphicsAdapter.DefaultAdapter.CurrentDisplayMode.Height));
In the load function I run this:
swordTexture = content.Load<Texture2D>("SOLDIER_Sword");
swordPosition = new Vector2(300, 0);
swordRefSize = new Vector2(557, 490);
swordSize = new Vector2(557, 490);
swordRefPosition = new Vector2(300, 0);
swordColor = Color.White;
sword = new StaticSprite(swordTexture, swordPosition, swordSize, swordColor);
In update everytime the screen resolution changes (I have buttons set to do that) this:
graphics.PreferredBackBufferHeight = setHeight;
graphics.PreferredBackBufferWidth = setWidth;
graphics.ApplyChanges();
swordPosition = CalculateNewPos(swordRefPosition, new Vector2(1920, 1080), new Vector2(setWidth, setHeight));
swordSize = CalculateNewSize(swordRefSize, new Vector2(1920, 1080), new Vector2(setWidth, setHeight));
And in draw:
batch.Draw(swordTexture, swordPosition, null, swordColor, 0f, Vector2.Zero, swordSize, SpriteEffects.None, 0f);
Sorry there is so much code, I'm really stumped and can't pinpoint where it's going wrong, so I just included everything that changes the variables.
Thank you so much for taking the time to look through this.
Note that current display mode is only meaningful if you are fullscreen. It's the VIEWPORT that tells you the width/height of spritebatch's projection matrix.
That said, you should really look into Bjarke's post, because you don't usually want to be rescaling every individual item.
Related
I have been working on a 2D physics engine using polygons.
And i am having trouble implementing the actual physics part. For a bit of background, i am not experienced at all when it comes to physics and therefor even if a found how to do the entire physics thing online, i would not be able to implement it into my project.
My goal is:
To have polygons fall with gravity.
Have weight drag etc.
Collision between multiple polygons.
What i have already made:
A way of displaying and creating multiple polygons.
Moving and rotating specified object(polygon).
Coeffients for drag, gravity and weight.
Hit boxes and visual boxes. (Visual boxes are what gets displayed and hit boxes are for physics)
A center point for every object. (So far is used for rotation)
A tick for when everything gets calculated. (Gametick/tickrate or whatever you wanna call it)
What i was not able to add / looking for:
Actual gravity.
Collision detection
Velocity for each object.
Collision between object.
Code snippets / how stuff works so far:
Beware that my code is janky and could be made better or more efficient.
Efficiency is not what im looking for!
Function for creating object:
public Object CreateNew(PointF[] hb, PointF[] vb, float rt, Color cl, bool gr, PointF ps)
{
Object obj = new Object
{
pos = ps,
rotation = rt,
offsets = vb,
hitBox = hb,
visBox = vb,
gravity = gr,
clr = cl,
};
#region center
List<Vector2> v2Points = new List<Vector2>();
foreach (PointF p in obj.offsets)
{
v2Points.Add(new Vector2(p.X, p.Y));
}
PointF point = ToPoint(Centroid(v2Points));
obj.center = new PointF(point.X, point.Y);
#endregion
return obj;
}
Function for changing position of object:
public Object ChangePosition(PointF pos, double rot, Object obj)
{
//////////////
int i = 0;
foreach (PointF p in obj.visBox)
{
float minPosX = (float)Math.Sqrt((Math.Pow(obj.center.X - pos.X, 2) + Math.Pow(0 - 0, 2)));
float minPosY = (float)Math.Sqrt((Math.Pow(obj.center.Y - pos.Y, 2) + Math.Pow(0 - 0, 2)));
obj.visBox[i] = new PointF(obj.offsets[i].X + pos.X, obj.offsets[i].Y + pos.Y);
i++;
}
i = 0;
foreach (PointF p in obj.hitBox)
{
float minPosX = (float)Math.Sqrt((Math.Pow(obj.center.X - pos.X, 2) + Math.Pow(0 - 0, 2)));
float minPosY = (float)Math.Sqrt((Math.Pow(obj.center.Y - pos.Y, 2) + Math.Pow(0 - 0, 2)));
obj.hitBox[i] = new PointF(obj.offsets[i].X + pos.X, obj.offsets[i].Y + pos.Y);
i++;
}
obj.pos = pos;
List<Vector2> v2Points = new List<Vector2>();
foreach (PointF p in obj.offsets)
{
v2Points.Add(new Vector2(p.X, p.Y));
}
PointF point = ToPoint(Centroid(v2Points));
obj.center = point;
List<Vector2> v2Points2 = new List<Vector2>();
foreach (PointF p in obj.hitBox)
{
v2Points2.Add(new Vector2(p.X, p.Y));
}
PointF point2 = ToPoint(Centroid(v2Points2));
obj.centerHitBox = point2;
obj.hitBox = RotatePolygon(obj.hitBox, obj.center, rotation * -1);
obj.visBox = RotatePolygon(obj.visBox, obj.center, rotation * -1);
obj.offsets = RotatePolygon(obj.offsets, obj.center, rotation * -1);
obj.hitBox = RotatePolygon(obj.hitBox, obj.center, rot);
obj.visBox = RotatePolygon(obj.visBox, obj.center, rot);
obj.offsets = RotatePolygon(obj.offsets, obj.center, rot);
rotation = rot;
return obj;
}
Pastebin link to object script:
https://pastebin.com/9SnG4vyj
I will provide more information or scripts if anybody needs it!
I am building a test 3D renderer in WinForms using the objects in System.Numerics such as Vector3 and Matrix4x4.
The object drawn is a point cloud, centered around (0,0,0), and rotated about the origin. Each node renders as dots on the screen. Here is what the 3D shape should look like
Fake Perspective
and more specifically when viewed from the front the perspective should be obvious with the blue dots that are further away from the eye to be at a smaller distance from the center
Fake Perspective
The pipeline is roughly as follows:
Rotation transformation
Matrix4x4 RY = Matrix4x4.CreateRotationY(ry);
Perspective transformation (fov=90, aspect=1.0f, near=1f, far=100f)
Matrix4x4 P = Matrix4x4.CreatePerspectiveFieldOfView(fov.Radians(), 1.0f, 1f, 100f);
Camera transformation
Matrix4x4 C = RY * P;
var node = Vector3.Transform(face.Nodes[i], C);
Project to 2D
Vector2 point = new Vector2(node.X, node.Y);
View transformation
Matrix3x2 S = Matrix3x2.CreateScale(height / scale, -height / scale);
Matrix3x2 T = Matrix3x2.CreateTranslation(width / 2f, height / 2f);
Matrix3x2 V = S*T
point = Vector2.Transform(point, V);
Pixel Coordinates & Render
PointF pixel = new PointF(point.X, point.Y);
e.Graphics.FillEllipse(brush,pixel.X - 2, pixel.Y - 2, 4, 4);
So what I am seeing is an orthographic projection.
Program Output
The blue nodes further away are not smaller as expected. Somehow the perspective transformation is being ignored.
So my question is my usage of Matrix4x4.CreatePerspectiveFieldOfView() correct in step #2? And is the projection from 3D to 2D in step #4 correct?
Steps #1, #5 and #6 seem to be working exactly as intended, my issue is with steps #2-#4 somewhere.
Example code to reproduce the issue
Form1.cs
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
public Shape Object { get; set; }
protected override void OnLoad(EventArgs e)
{
base.OnLoad(e);
this.Object = Shape.DemoShape1();
}
protected override void OnPaint(PaintEventArgs e)
{
base.OnPaint(e);
float width = ClientSize.Width, height = ClientSize.Height;
float scale = 40f, fov = 90f;
Matrix4x4 RY = Matrix4x4.CreateRotationY(ry);
Matrix4x4 RX = Matrix4x4.CreateRotationX(rx);
Matrix4x4 P = Matrix4x4.CreatePerspectiveFieldOfView(fov.Radians(), 1.0f, 1f, 100f);
Matrix4x4 C = RY * RX * P;
Matrix3x2 S = Matrix3x2.CreateScale(
height / scale, -height / scale);
Matrix3x2 T = Matrix3x2.CreateTranslation(
width / 2f, height / 2f);
Matrix3x2 V = S * T;
using (var pen = new Pen(Color.Black, 0))
{
var arrow = new AdjustableArrowCap(4f, 9.0f);
pen.CustomEndCap = arrow;
using (var brush = new SolidBrush(Color.Black))
{
// Draw coordinate triad (omited)
// Each face has multiple nodes with the same color
foreach (var face in Object.Faces)
{
brush.Color = face.Color;
PointF[] points = new PointF[face.Nodes.Count];
for (int i = 0; i < points.Length; i++)
{
// transform nodes into draw points
var item = Vector4.Transform(face.Nodes[i], C);
var point = Vector2.Transform(item.Project(), V);
points[i] = point.ToPoint();
}
// Draw points as dots
e.Graphics.SmoothingMode = SmoothingMode.HighQuality;
for (int i = 0; i < points.Length; i++)
{
e.Graphics.FillEllipse(brush,
points[i].X - 2, points[i].Y - 2,
4, 4);
}
}
}
}
}
}
GraphicsExtensions.cs
public static class GraphicsExtensions
{
public static PointF ToPoint(this Vector2 vector)
=> new PointF(vector.X, vector.Y);
public static Vector2 Project(this Vector3 vector)
=> new Vector2(vector.X, vector.Y);
public static Vector2 Project(this Vector4 vector)
=> new Vector2(vector.X, vector.Y);
public static float Radians(this float degrees) => (float)(Math.PI/180) * degrees;
public static float Degrees(this float radians) => (float)(180/Math.PI) * radians;
}
I'm trying to scale a CCSprite to any resolution in CocosSharp, this is what I've got:
void AddTruck ()
{
var spriteSheet = new CCSpriteSheet ("animations/truck.plist");
var animationFrames = spriteSheet.Frames.FindAll ((x) => x.TextureFilename.StartsWith ("frame"));
walkAnim = new CCAnimation (animationFrames, 0.1f);
walkRepeat = new CCRepeatForever (new CCAnimate (walkAnim));
truck = new CCSprite (animationFrames.First ()) { Name = "Truck" };
truck.Scale = 0.70f;
AddChild (truck);
}
And I want that when its added to scene, it be resized according to the device resolution... Any tips?
Thanks.
Well, I think that in order to do that, you have to have another folder with HD images, and acording to the device resolution, you use 'em... But I did the following, and it worked for me:
float ResTruck(CCSprite sprite)
{
float scale = 0.0f;
float resWid = CCScene.DefaultDesignResolutionSize.Width;
float resHei = CCScene.DefaultDesignResolutionSize.Height;
if (resWid > 500)
scale = 0.70f;
else if (resWid > 1080)
scale = 0.90f;
else if (resWid > 1300)
scale = 1.0f;
return scale;
}
And then, I asing the "scale" value to my sprite:
float scale = ResTruck(truck);
truck.Scale = scale;
And that's it :v
I currently have an asteroid texture loaded as my "test player" for the game I'm writing. What I'm trying to figure out how to do is get a triangle to shoot from the center of the asteroid, and keep going until it hits the top of the screen. What happens in my case (as you'll see from the code I've posted), is that the triangle will show, however it will either be a long line, or it will just be a single triangle which stays in the same location as the asteroid moving around (that disappears when I stop pressing the space bar), or it simply won't appear at all. I've tried many different methods, but I could use a formula here.
All I'm trying to do is write a space invaders clone for my final in C#. I know how to code fairly well, my formulas just need work is all.
So far, this is what I have:
Main Logic Code
protected override void Draw(GameTime gameTime)
{
GraphicsDevice.Clear(ClearOptions.Target, Color.Black, 1, 1);
mAsteroid.Draw(mSpriteBatch);
if (mIsFired)
{
mPositions.Add(mAsteroid.LastPosition);
mRay.Fire(mPositions);
mIsFired = false;
mRay.Bullets.Clear();
mPositions.Clear();
}
base.Draw(gameTime);
}
Draw Code
public void Draw()
{
VertexPositionColor[] vertices = new VertexPositionColor[3];
int stopDrawing = mGraphicsDevice.Viewport.Width / mGraphicsDevice.Viewport.Height;
for (int i = 0; i < mRayPos.Length(); ++i)
{
vertices[0].Position = new Vector3(mRayPos.X, mRayPos.Y + 5f, 10);
vertices[0].Color = Color.Blue;
vertices[1].Position = new Vector3(mRayPos.X - 5f, mRayPos.Y - 5f, 10);
vertices[1].Color = Color.White;
vertices[2].Position = new Vector3(mRayPos.X + 5f, mRayPos.Y - 5f, 10);
vertices[2].Color = Color.Red;
mShader.CurrentTechnique.Passes[0].Apply();
mGraphicsDevice.DrawUserPrimitives<VertexPositionColor>(PrimitiveType.TriangleStrip, vertices, 0, 1);
mRayPos += new Vector2(0, 1f);
mGraphicsDevice.ReferenceStencil = 1;
}
}
This isn't quite how you're supposed to be manipulating the location of a model in world space and since you're creating a new vertex array every single draw frame you'll find that it performs pretty badly when you come to draw more than a few triangles.
declare the vertices and index list for your triangle just once in the LoadContent method.
VertexBuffer triangleVertexBuffer;
IndexBuffer triangleIndexBuffer;
protected override void LoadContent()
{
// Setup a basic effect to draw the triangles with.
mEffect = new BasicEffect(GraphicsDevice);
// setup the triangle vertext buffer and load up it's content.
triangleVertexBuffer = new VertexBuffer(GraphicsDevice, typeof(VertexPositionColor), 3, BufferUsage.WriteOnly);
triangleVertexBuffer.SetData<VertexPositionColor>(new VertexPositionColor[]
{
new VertexPositionColor (new Vector3 (0f, -1f, 0.0f), Color.Blue), // Top Point
new VertexPositionColor (new Vector3 (-1f, 1f, 0.0f), Color.White), // Bottom Left
new VertexPositionColor (new Vector3 (1f, 1f, 0.0f), Color.Red), // Bottom Right
});
// setup an index buffer to join the dots!
triangleIndexBuffer = new IndexBuffer(GraphicsDevice, IndexElementSize.SixteenBits, 3, BufferUsage.WriteOnly);
triangleIndexBuffer.SetData<short>(new short[]
{
0,
1,
2,
});
}
After this assuming your effect takes in to account a world transformation (basic effect does) you can use that parameter to move the triangle.
protected override void Draw(GameTime gameTime)
{
GraphicsDevice.Clear(Color.CornflowerBlue);
for (int i = 0; i < mRayPos.Length ; i++)
{
// This is the line that moves the triangle along your ray.
mEffect.World = Matrix.CreateTranslation(new Vector3(mRayPos[i].X, mRayPos[i].Y, mRayPos[i].Z));
mEffect.Techniques[0].Passes[0].Apply();
// These lines tell the graphics card that you want to draw your triangle.
GraphicsDevice.SetVertexBuffer(triangleVertexBuffer);
GraphicsDevice.Indices = triangleIndexBuffer;
GraphicsDevice.DrawIndexedPrimitives(PrimitiveType.TriangleList, 0, 0, 3, 0, 1);
}
base.Draw(gameTime);
}
If you use this method it becomes a very simple operation to rotate or scale your trangle using Matrix.CreateRotation and Matrix.CreateScale.
I have this code:
public class Area
{
Texture2D point;
Rectangle rect;
SpriteBatch _sB;
GameTimer _gt;
int xo, yo, xt, yt;
//List<Card> _cards;
public Area(Texture2D point, SpriteBatch sB)
{
this.point = point;
this._sB = sB;
xt = 660;
yt = 180;
xo = 260;
yo = 90;
}
public void Draw(SpriteBatch spriteBatch)
{
rect = new Rectangle(660, 180, 80, 120);
spriteBatch.Draw(point, rect, Color.White);
_gt = new GameTimer();
_gt.UpdateInterval = TimeSpan.FromSeconds(0.1);
_gt.Draw += OnDraw;
}
private void OnDraw(object sender, GameTimerEventArgs e)
{
this.pass(xo, yo);
if (xo != xt) xo += (xt > xo) ? 10 : -10;
if (yo != yt) yo += (yt > yo) ? 10 : -10;
}
public void pass(int x, int y)
{
rect = new Rectangle(x, y, 80, 120);
_sB.Draw(point, rect, Color.Black);
}
}
So, I can't understand what's wrong. And It's my first project with XNA, and because of it there can be stupid mistake :)
P.S. Sorry. There is a rectangle with coordinates (xt,yt), and I need the animation to move the rectangle to (xo,yo)
P.P.S. I added the full class with corrections, because I don't understand my mistake.
You are drawing the entire animation in one frame.. .you should call Pass with diferent x,y from OnDraw...
EDITED:
1) You don't need the timer, the draw method in game class is by default called 60 frames per second...
2) The Seconds parameter should be calculated as (float) gametime.ElapsedTime.TotalSeconds;
float time;
int xt=660, yt=180;
int xo=260, yo=90;
public void Draw(SpriteBatch spriteBatch, float Seconds)
{
rect = new Rectangle(660, 180, 80, 120);
spriteBatch.Draw(point, rect, Color.White);
this.pass(xo, yo, spriteBatch);
time+= Seconds;
if (time>0.3)
{
if (xo!=xt) xo+= (xt>xo) ? 10 : -10;
if (yo!=yt) yo+= (yt>yo) ? 10 : -10;
time = 0;
}
}
public void pass(int x, int y, spritebatch sb)
{
rect = new Rectangle(x, y, 80, 120);
sb.Draw(point, rect, Color.Red);
}
As you should know this animation will move in a rough mode... if you want to move your sprite smoothly... you can use a Vector2 for your positions and a float for your speed;
Vector2 Origin = new Vector2(260, 90);
Vector2 Target = new Vector2(660, 180);
Vector2 Forward = Vector2.Normalize(Target-Source);
float Speed = 100; // Pixels per second
float Duration = (Target - Origin).Length() / Speed;
float Time = 0;
public void Update(float ElapsedSecondsPerFrame)
{
if (Time<Duration)
{
Time+=Duration;
if (Time > Duration) {
Time = Duration;
Origin = Target;
}
else Origin += Forward * Speed * ElapsedSecondsPerFrame;
}
}
public void Draw()
{
rect = new Rectangle((int) Origin.X, (int) Origin.Y, 80, 120);
sb.Draw(point, rect, Color.Red);
}
If you are willing to use Sprite Vortex to make your animations (a specific version actually) you can use the following class. You have to use Sprite Vortex 1.2.2 because in the newer versions the XML format is changed. Make sure that the XML file you add the property is changed to "Do not compile".
If you need a working example I can send you a very simple one.
p.s. Sprite Vortex should do the same thing you use the other program for, however v 1.2.2 is pretty buggy but not too bad.
the class is here : http://pastebin.com/sNSa7xgQ
Use Sprite Vortex (make sure it's 1.2.2) to select a spritesheet and select the sub images you want to animate. export the XML code.
add the class to your project, it reads the XML and adds automatically creates the frames for the animation for you.