Drawing multiple sprites with a for loop xna 4.0 - c#

I am currently working on a platforming game and I am trying to draw 40 items in one screenstate, but I don't want to hard code them all. Here's what I've tried so far:
Sprite class:
class Sprite
{
//texture, position and color
public Texture2D texture;
public Vector2 position;
public Color color;
}
Definiton:
Sprite levelboxbg;
int LevelBoxX = 20;
Loading:
levelboxbg = new Sprite();
levelboxbg.texture = Content.Load<Texture2D>("LevelBoxBeta");
levelboxbg.position = new Vector2(0, 0);
levelboxbg.color = Color.White;
Execution:
public void DrawLevelBoxes(SpriteBatch spriteBatch)
{
for (int i = 0; i < 10; i++)
{
spriteBatch.Draw(levelboxbg.texture, new Vector2(LevelBoxX + 20 ,0), levelboxbg.color);
LevelBoxX += 20;
}
}
I then call the method in my draw function.
Visual studio has given me 0 errors for this and it will run; however, when I get to the screen where it's supposed to draw the boxes, it draws them all but only for a fraction of a second, then they dissipate.
Any help is greatly appreciated, thank you for taking the time to read this.

Your LevelBoxX goes to infinity, so the boxes are running out of the screen pretty fast. You can reset LevelBoxX just before the for-loop like so:
public void DrawLevelBoxes(SpriteBatch spriteBatch)
{
LevelBoxX = 20;
for (int i = 0; i < 10; i++)
{
spriteBatch.Draw(levelboxbg.texture, new Vector2(LevelBoxX + 20 ,0), levelboxbg.color);
LevelBoxX += 20;
}
}
Or just declare a local variable:
public void DrawLevelBoxes(SpriteBatch spriteBatch)
{
int counter = 20;
for (int i = 0; i < 10; i++)
{
spriteBatch.Draw(levelboxbg.texture, new Vector2(counter + 20 ,0), levelboxbg.color);
counter += 20;
}
}

Related

Tube mesh generation in unity

I've created a procedural mesh script while watching one of Freya Holmér's improvised live course vods, and re purposed the code to create a procedural tube mesh with subdivision and plenty of other niche features.
But, after looking over the code and the lesson, I still cannot for the life of me figure out why sometimes I will get an:
argument out of range exception
...and sometimes I won't depending on the level of subdivision; also, entire faces wont be generated by the script.
TL;DR Problems list:
Argument out of range exception (Depending on level of subdivision).
Entire sides/faces of the tube mesh will not be generated, even when no errors are provided.
These problems are stemming from line 154 in the UpdateTris() method.
Code
using System.Collections.Generic;
using UnityEngine;
#if UNITY_EDITOR
using UnityEditor;
using OpenNexus.ExtraGizmos;
#endif
using OpenNexus.BaseMath;
using OpenNexus.MeshFormat;
namespace OpenNexus.Procedurals
{
[RequireComponent(typeof(MeshFilter))]
public class TubeMesh : MonoBehaviour
{
private MeshFilter filter;
private Mesh mesh;
public List<Vector3> Points = new List<Vector3>()
{
new Vector3(0, 0, 0),
new Vector3(0, 1, 1),
new Vector3(0, 0, 2)
};
[Header("Bezier Data")]
public Precision BezierPrecision = Precision.Fast;
[Header("Tube Data")]
[Range(0.01f, 1f)]public float Radius = 0.01f;
[Range(3, 32)] public int Segments = 8;
[Range(3, 6)] public int Subdivisions = 3;
[Header("Mesh Data")]
public ProceduralMesh2D BaseProceduralMesh = new ProceduralMesh2D();
public List<Vector3> Vertices;
public List<int> Tris;
private void OnEnable()
{
if (!GetComponent<MeshRenderer>()) // Check for and add missing mesh renderer.
gameObject.AddComponent<MeshRenderer>();
if (filter == null) // Check for and assign mesh filter variable.
filter = GetComponent<MeshFilter>();
if (mesh == null) // Check for and instantiate a new mesh object.
mesh = new Mesh();
mesh.name = "TubeMesh"; // Set name to mesh object.
filter.sharedMesh = mesh; // Set MeshFilter's shared mesh variable to new mesh object.
}
private void Update()
{
/*
Data reset
------------------------------
*/
// Reset base mesh data.
BaseProceduralMesh.Vertices = new List<Vertex>();
BaseProceduralMesh.LineIndex = new List<int>();
// Reset mesh data.
Vertices = new List<Vector3>();
Tris = new List<int>();
/*
Data update
------------------------------
*/
// Update base mesh.
UpdateBaseMesh();
// Update mesh data.
UpdateVertices();
UpdateTris();
}
private void LateUpdate() => UpdateMesh();
private BezierPoint GetBezierPoint(int index)
{
float _t = index / (Segments - 1f);
BezierPoint _bp = BezierMath.QuadraticBezier(Points, BezierPrecision, _t);
return _bp;
}
private void UpdateBaseMesh()
{
// Generate base vertices.
for (int i = 0; i < Subdivisions; i++)
{
float _point = i / (float)Subdivisions;
float _radius = _point * Floats.TAU;
Vertex _vertex = new Vertex(VectorThrees.UnitVectorByAngle(_radius) * Radius);
BaseProceduralMesh.Vertices.Add(_vertex);
}
// Generate base LineIndexes.
for (int i = 0; i < BaseProceduralMesh.VertexCount; i++)
{
BaseProceduralMesh.LineIndex.Add(i);
}
BaseProceduralMesh.LineIndex.Add(0);
}
private void UpdateVertices()
{
for (int i = 0; i < Segments; i++)
{
BezierPoint _point = GetBezierPoint(i);
for (int j = 0; j < BaseProceduralMesh.VertexCount; j++)
{
Vertices.Add(_point.LocalToWorldPosition(BaseProceduralMesh.Vertices[j].Point));
}
}
}
private void UpdateTris()
{
for (int s = 0; s < Segments - 1; s++)
{
int _root = s * BaseProceduralMesh.VertexCount;
int _rootNext = (s + 1) * BaseProceduralMesh.VertexCount;
for (int i = 0; i < BaseProceduralMesh.EdgeCount; i+=2)
{
int _lineA = BaseProceduralMesh.LineIndex[i];
int _lineB = BaseProceduralMesh.LineIndex[i + 1];
int _currentA = _root + _lineA;
int _currentB = _root + _lineB;
int _nextA = _rootNext + _lineA;
int _nextB = _rootNext + _lineB;
Tris.Add(_currentA);
Tris.Add(_nextA);
Tris.Add(_nextB);
Tris.Add(_currentA);
Tris.Add(_nextB);
Tris.Add(_currentB);
}
}
}
private void UpdateMesh()
{
mesh.Clear();
mesh.SetVertices(Vertices);
mesh.SetTriangles(Tris, 0);
mesh.RecalculateNormals();
}
#if UNITY_EDITOR
private void OnDrawGizmos()
{
// Draw psudo mesh with gizmos.
/*
Draw segment/edge loops
-------------------------------------
*/
for (int i = 0; i < Segments; i++) // Debug each segment, and what their 2D mesh segment should look like.
{
BezierPoint _point = GetBezierPoint(i);
WireGizmos.DrawWireCircle(_point.Position, _point.Rotation, Radius, Subdivisions);
}
Gizmos.color = Color.red;
for (int i = 0; i < Vertices.Count; i++) // Debug each vertex.
{
Gizmos.DrawSphere(Vertices[i], 0.01f);
Handles.Label(Vertices[i], "\n\nVertex: " + i + "\n\nVertex position:\n" + Vertices[i].ToString());
}
for (int i = 0; i < Tris.Count; i++)
{
Gizmos.DrawLine(Vertices[Tris[i]], Vertices[Tris[(i + 1) % Tris.Count]]);
}
}
#endif
}
}
I've already looked over my code compared to the code in the video, it's fundamentally the same, with the main differences being, how I'm creating the 2D mesh format for the script to work with, and the structure of the code.
After looking back at what they had done compared to mine, I just don't see how I'm running into this issue.
Things I've tried
Change the loop iteration i+=2 -to- i++ (This spits out the exception, but generates the first section of the tube before getting stuck between the second vertex in the next segment, and vert 0).
Every suggestion I try from this question, I will update the "Things I've tried..." list.
Please let me know if I need to clarify anything in this post, and if you wish to see the other classes/structs referenced in this script.
After a day of resting, and the a couple of hours of testing different values and questioning the code. I've found the problem.
The real issue
The nested for loop in the UpdateTris() method was going up by twos assuming I was making a hard edge mesh with pared vertices. This caused the loop to skip over an entire face of the mesh, and causing the
ArgumentOutOfRangeException
...when the Subdivision value was an even number.
My solution
I had to bring it back to the default single iteration (since I was trying to make a smooth lighting mesh), the second issue with the for loop was the iteration limit BaseProceduralMesh.LineCount needing to be subtracted by one since that was causing another
ArgumentOutOfRangeException

How to set the size of LineRenderer from script?

Hello :) My code below works fine. It can create a line renderer if you add gameobjects manually. What I want to know though is how to set the size dynamically of my points. Here's a sample pic of what I'm talking about. On the bottom of the inspector, I can set the size of my points, sure. What I'm trying to do is how to code the size as, example 10 and not through the inspector.
How can I set the size of the points of the line renderer?
Here is my code:
public class Example : MonoBehaviour {
LineRenderer lineRenderer;
public Transform[] points;
private Vector3[] vP;
int seg;
// Use this for initialization
void Start () {
lineRenderer = GetComponent<LineRenderer>();
Lines();
}
public void Lines() {
seg = points.Length;
vP = new Vector3[points.Length];
for (int i = 0; i < points.Length; i++)
{
vP[i] = points[i].position;
}
for (int i = 0; i < seg; i++)
{
float t = i / (float)seg;
lineRenderer.numPositions = vP.Length;
lineRenderer.SetPositions(vP);
}
}
}
The LineRenderer.startWidth and LineRenderer.endWidth variabls are used to set the size of the LineRenderer.
The 0.2f value seems to be fine for this.
public void Lines()
{
lineRenderer.startWidth = 0.2f;
lineRenderer.endWidth = 0.2f;
seg = points.Length;
vP = new Vector3[points.Length];
for (int i = 0; i < points.Length; i++)
{
vP[i] = points[i].position;
}
for (int i = 0; i < seg; i++)
{
float t = i / (float)seg;
lineRenderer.numPositions = vP.Length;
lineRenderer.SetPositions(vP);
}
}
You can also use the LineRenderer.widthMultiplier variable but that's not necessary.
EDIT:
You just want to set the size of the points variable from code instead of Editor. Just do this:
int size = 10;
points = new Transform[size];
then loop over the points variable and fill the point's transform
for (int i = 0; i < points.Length; i++)
{
points[i] = theGameObjectTransformToUse.transform;
}
If you don't have a Transform to fill it, you can create dummy GameObjects and use their transforms them to fill it.
for (int i = 0; i < points.Length; i++)
{
GameObject tempObj = new GameObject("dummy" + i);
tempObj.transform.position = yourNewPostion;
points[i] = tempObj.transform;
}
Note:
lineRenderer.numPositions = vP.Length; should be outside and before the for loop. It is unnecessary to do that in the for loop. You only need to set that once.

How to create a 3D Grid of vectors without a memory exception -Unity 3D

Below is a few simple lines of code that is part of uni tech demo. In an attempt to create a 3D grid of vectors within a given area.
My solution thus far is to create a 2D grid at the starting X and Y points and then repeat this process along the Z.
As a temporary visualization I then instantiate Sphere prefabs.
The purpose of which is to use this grid of vectors as a model for a depth first search path algorithm which I will use to input vectors for a procedurally generated track(currently control points are set manually via editor methods)
using UnityEngine;
using System.Collections;
using System.Collections.Generic;
public class GridLayout : MonoBehaviour {
public int GridWidth;
public int GridLength;
public int GridHeight;
public int resolution;
private int ResW;
private int ResL;
private int ResH;
private List<Vector3> GridPoints = new List<Vector3>();
private bool GridCompleted = false;
private GameObject tempObject;
// Use this for initialization
void Start () {
//Area box Start square
GridPoints.Add(new Vector3(0,0,0));
GridPoints.Add(new Vector3(0,GridHeight,0));
GridPoints.Add(new Vector3(GridWidth,0,0));
GridPoints.Add(new Vector3(GridWidth,GridHeight,0));
ResW = GridWidth/resolution;
ResH = GridHeight/resolution;
ResL = GridLength/resolution;
}
// Update is called once per frame
void Update () {
if (GridCompleted == false)
CreateGrid();
else
{
for(int i = 0; i <= GridPoints.Count; i++)
{
tempObject = GameObject.CreatePrimitive(PrimitiveType.Sphere);
tempObject.transform.position = GridPoints[i];
}
}
} //Area Box End square
// GridPoints.Add(new Vector3(0,0,GridLength));
// GridPoints.Add(new Vector3(0,GridHeight,GridLength));
// GridPoints.Add(new Vector3(GridWidth,0,GridLength));
// GridPoints.Add(new Vector3(GridWidth,GridHeight,GridLength));
void CreateGrid()
{
if(ResW != GridWidth | ResL != GridLength | ResH != GridHeight)
{
for(int l = 1;ResL <= GridLength; l++)
{
GridPoints.Add (new Vector3(0,0,ResL));
ResL = (GridLength/(resolution))*l;
for(int w = 1;ResW <= GridWidth; w++)
{
GridPoints.Add (new Vector3(ResW,0,0));
ResW = (GridWidth/(resolution))*w;
for(int h = 1;ResW <= GridHeight; h++)
{
GridPoints.Add (new Vector3(0,ResH,0));
ResH = (GridHeight/(resolution))*h;
}
}
}
}
else
{
GridCompleted = true;
}
}
}
Unfortunately this triple for loop leads to a memory exception - this is a high end PC however I will be forced to run my project on a laptop with 4GB ram.
With that in mind : is there a more memory efficient way of creating a vector3 grid.
Thanks in advance.
Typing failure in line
for(int h = 1;ResW <= GridHeight; h++)
Is causing your memory problems. The most inner loop runs infinitely, it should be ResH. Therefore I suggest to check the for-variable inside the for-statement and not something else:
for(int h = 1; h < wharever; h++)
secondly: bad code formatting and indentation.
Finally, so far, a list<object> is a 1D structure. A 3rd structure would be a list<list<list<object>>> or a array object [][][]:
Vector3 [][][] vector3grid;
vector3grid = new vector3[lenX][][];
for (int x=0; x<lenX; x++)
{
vector3grid[x] = new vector3 [lenY][];
for (int y=0; y<lenY; y++)
{
vector3grid[x][y] = new vector3 [lenZ];
// init if needed:
for(int z=0; ...
vector3grid[x][y][z] = ...
}
}
Edit:
I just noticed that my answer is not 100% correct. The sample above is 1 of 2 (or more) ways to create a 3D array. The easier one of them is following:
In C++/cli:
Array<vector3, 3>^ vector3grid = gcnew array<vector3, 3>(lenX, lenY, lenZ);
For c# and VB.net I need to look up the Syntax first.
This is a REAL 3D array now. ;-)
Edit 2:
3D in c#:
Vector3 [,,] vector3grid = New vector3[lenX,lenY,lenZ];

XNA invader shooting in space invaders

so I was told to write a code and then post my problem. So I did.
this is the invader shooting code:
Texture2D Background;
Texture2D invader;
Texture2D invaderBullet;
Rectangle rectInvaderBullet;
Rectangle[,] rectInvader;
bool[,] isInvaderAlive;
const int rows = 5;
const int cols = 10;
bool invaderBulletVisible = false;
const int bulletSpeed = 5;
int invaderSpeed = 2;
int invadersAliveCounter;
Update:
`//invader shooting thinking begin
for (int r = 0; r < rows; r++)
for (int c = 0; c < cols; c++)
{
int check = rnd.Next(0, 1000);
if (check < 800 && invaderBulletVisible == false)
{
invaderBulletVisible = true;
rectInvaderBullet.X = rectInvader[r, c].X + (rectInvader[r, c].Width / 2) - (rectInvaderBullet.Width / 2);
rectInvaderBullet.Y = rectInvader[r, c].Y - rectInvaderBullet.Height + 35;
}
if (invaderBulletVisible)
rectInvaderBullet.Y = rectInvaderBullet.Y + bulletSpeed;
}
//invader shooting thinking end`
and this is the draw code:
if (invaderBulletVisible)
spriteBatch.Draw(invaderBullet, rectInvaderBullet, Color.White);
Now of course something is wrong. First off I'm an amatuer and I haven't even finished school but I'm trying my best. Secondly, the bullet isn't even appearing. The bullet isn't even appearing. So basically I want to understand why. And also, how do I make it so that only an invader with no one infront of it can shoot?
Thank you so much in advance!!!

Colour spiral not working in XNA

I am making a simple process which will create a colour changing spiral so I can test a range of variables and learn some C#. However, though I can not find a problem in the code, when I debug it, I am returned with a blank blue screen. Can any one find the problem. Here is all the code containing the variables needed for the ball point:
Vector2 Tripos;
List<Color> Tricol;
List<Vector2> datatripos;
List<int> count;
Color currentcol;
float Tri_angle;
float Triscale;
int Tri_speed;
int screenwidth;
int screenheight;
int addtocount;
Texture2D Ball;
int colourchangespeed;
protected override void LoadContent()
{
// Create a new SpriteBatch, which can be used to draw textures.
Tripos = new Vector2(screenwidth / 2, screenheight / 2);
Triscale = 1;
Tri_angle = 1;
Tri_speed = 1;
colourchangespeed = 1;
Ball = Content.Load<Texture2D>("ball");
currentcol = new Color(0, 0, 0);
addtocount = 0;
datatripos = new List<Vector2>();
Tricol = new List<Color>();
}
This is called by the update method:
private void Posgen()
{
Tripos.X += (float)Math.Sin(MathHelper.ToRadians(Tri_angle))*Tri_speed;
Tripos.Y += (float)Math.Cos(MathHelper.ToRadians(Tri_angle))*Tri_speed;
Tri_angle++;
}
private void colchanger()
{
currentcol.R += (byte)colourchangespeed;
if (currentcol.R == 255)
{
currentcol.R = 0;
currentcol.G += (byte)colourchangespeed;
}
if (currentcol.G == 255)
{
currentcol.G = 0;
currentcol.B += (byte)colourchangespeed;
}
if (currentcol.B == 255)
{
currentcol.B = 0;
}
}
private void dataadd()
{
addtocount++;
Tricol.Add(currentcol);
datatripos.Add(Tripos);
count.Add(addtocount);
}
Called by the draw method:
private void drawtri()
{
foreach (Vector2 data in datatripos)
{
spriteBatch.Draw(Ball, data, null, currentcol, 0, new Vector2(5, 5), Triscale, SpriteEffects.None, 0);
}
}
If you want the full code ask in advance. There are some variables I don't use but I intend to use for later so ignore these.
Thanks in advance.
Yours Mona.
Check that you are calling dataadd().
As a precaution, colourchangespeed should be declared as a byte rather than as an int because you're only casting to a byte anyway. When checking you colour values, the current value should be compared as >= 255 in the case that colourchangespeed is not 1.
A tip to writing good questions: Remove everything that's unrelated. Everything.

Categories