Movement very fast - c#

I have a monogame project, where I want to move the player based on the the keyboard input. But my code just makes the movement super fast.
I tried around with different speed limits and checked if it might work if with different GameTime properties.
Where is the problem with my code?
public class Map {
private Map() {
Position = new Vector2(0, 0);
}
public string Data { get; set; }
public string[][] MapData { get; set; }
public ContentManager Content { get; set; }
public SpriteBatch SpriteBatch { get; set; }
public Vector2 Position { get; set; }
private Vector2 ArrayPosition;
private readonly int Speed_X = 40;
private readonly int Speed_Y = 32;
public static Map Parse(string path) {
var map = new Map();
var stream = TitleContainer.OpenStream(Path.Combine("Content", path));
using (var sr = new StreamReader(stream)) {
map.Data = sr.ReadToEnd();
}
var lines = map.Data.Split(new string[1] { Environment.NewLine }, StringSplitOptions.None);
var mapHeight = lines.Count();
map.MapData = new string[mapHeight][];
for (int i = 0; i < lines.Count(); i++) {
var elements = lines[i].Split(';');
map.MapData[i] = elements;
}
return map;
}
public void DrawMap(SpriteBatch spriteBatch, ContentManager content, GameTime gametime) {
this.SpriteBatch = spriteBatch;
this.Content = content;
for (int y = 0; y < MapData.Count(); y++) {
var current = MapData[y];
for (int x = 0; x < current.Count(); x++) {
switch (current[x]) {
case "e":
drawEnemy(x, y);
break;
case "P":
case ".":
drawTile(x, y);
break;
case "w":
drawWall(x, y);
break;
}
}
}
drawPlayer();
}
public void Move(Direction pdirection, GameTime gametime) {
var direction = Vector2.Zero;
var speed = Vector2.Zero;
var y = Math.Floor(this.ArrayPosition.Y);
var x = Math.Floor(this.ArrayPosition.X);
switch (pdirection) {
case Direction.Up:
if (y > 0 && y < 16) {
direction = new Vector2(0, -1);
speed.Y = Speed_Y;
}
break;
case Direction.Down:
if (y < 16 && y >= 0) {
direction = new Vector2(0, 1);
speed.Y = Speed_Y;
}
break;
case Direction.Left:
if (x > 0 && x < 16) {
direction = new Vector2(-1, 0);
speed.X = Speed_X;
}
break;
case Direction.Right:
if (x < 16 && x >= 0) {
direction = new Vector2(1, 0);
speed.X = Speed_X;
}
break;
}
ArrayPosition = (this.Position + (direction * speed)) / new Vector2(Speed_X, Speed_Y);
var newPosition = this.Position + (direction * speed * gametime.ElapsedGameTime.Milliseconds);
if (this.MapData[(int)Math.Floor(ArrayPosition.Y)][(int)Math.Floor(ArrayPosition.X)] != "w") {
this.Position = newPosition;
}
}
private void drawPlayer() {
var x = Position.X;
var y = Position.Y;
drawTile((int)x, (int)y);
var texture = Content.Load<Texture2D>("Sprites/player");
this.SpriteBatch.Draw(texture, new Vector2(x, y), Color.White);
}
private void drawEnemy(int x, int y) {
drawTile(x, y);
drawTexture(Content.Load<Texture2D>("Sprites/enemy"), x, y);
}
private void drawTile(int x, int y) {
drawTexture(Content.Load<Texture2D>("Tiles/grass"), x, y);
}
private void drawWall(int x, int y) {
drawTexture(Content.Load<Texture2D>("Tiles/wall"), x, y);
}
private void drawTexture(Texture2D texture, int x, int y) {
var rectangle = new Rectangle(x * 40, y * 32, 40, 32);
this.SpriteBatch.Draw(texture, rectangle, Color.White);
}
}

You should be using the TotalSeconds property instead of Milliseconds in GameTime.ElapsedGameTime. The latter is an int which is not useful for fractional computations whereas the former is a double which is. Due to it being an int it also explains why your movement is very fast.
In your Move() method change this line:
var newPosition = this.Position +
(direction * speed *
gametime.ElapsedGameTime.Milliseconds);
...to:
var newPosition = this.Position +
(direction * speed *
gametime.ElapsedGameTime.TotalSeconds);
However contrary to what the other poster says, it is not necessary to perform a deltatime=0; because ElapsedGameTime is defined as:
The amount of elapsed game time since the last update - MSDN
You don't want to go resetting time intervals as they will just lead to animation which is not particularly smooth.
Tell me more
Your First Game - XNA Game Studio in 2D

use something like this in function Move:
float deltatime=0;
public void Move(Direction pdirection, GameTime gametime) {
deltaTime= (float)gameTime.ElapsedGameTime.TotalSeconds;
//calculate your object position using deltatime
}

Related

Why linerenderer is not changing colors at runetime?

The first script is attached to some object for example a cube and draw a circle around the cube :
using UnityEngine;
using System.Collections;
using System.Collections.Generic;
[ExecuteAlways]
[RequireComponent(typeof(UnityEngine.LineRenderer))]
public class DrawCircle : MonoBehaviour
{
[Range(1, 50)] public int segments = 50;
[Range(1, 500)] public float xRadius = 5;
[Range(1, 500)] public float yRadius = 5;
[Range(0.1f, 5)] public float width = 0.1f;
[Range(0, 100)] public float height = 0;
public bool controlBothXradiusYradius = false;
public bool draw = true;
[SerializeField] private LayerMask targetLayers;
[SerializeField] private LineRenderer line;
private void Start()
{
if (!line) line = GetComponent<LineRenderer>();
if (draw)
CreatePoints();
}
private void Update()
{
if (Physics.CheckSphere(transform.position, xRadius, targetLayers))
{
Debug.Log("player detected");
}
else
{
Debug.Log("player NOT detected");
}
}
public void CreatePoints()
{
line.enabled = true;
line.widthMultiplier = width;
line.useWorldSpace = false;
line.widthMultiplier = width;
line.positionCount = segments + 1;
float x;
float y;
var angle = 20f;
var points = new Vector3[segments + 1];
for (int i = 0; i < segments + 1; i++)
{
x = Mathf.Sin(Mathf.Deg2Rad * angle) * xRadius;
y = Mathf.Cos(Mathf.Deg2Rad * angle) * yRadius;
points[i] = new Vector3(x, height, y);
angle += (380f / segments);
}
// it's way more efficient to do this in one go!
line.SetPositions(points);
}
#if UNITY_EDITOR
private float prevXRadius, prevYRadius;
private int prevSegments;
private float prevWidth;
private float prevHeight;
private void OnValidate()
{
// Can't set up our line if the user hasn't connected it yet.
if (!line) line = GetComponent<LineRenderer>();
if (!line) return;
if (!draw)
{
// instead simply disable the component
line.enabled = false;
}
else
{
// Otherwise re-enable the component
// This will simply re-use the previously created points
line.enabled = true;
if (xRadius != prevXRadius || yRadius != prevYRadius || segments != prevSegments || width != prevWidth || height != prevHeight)
{
CreatePoints();
// Cache our most recently used values.
prevXRadius = xRadius;
prevYRadius = yRadius;
prevSegments = segments;
prevWidth = width;
prevHeight = height;
}
if (controlBothXradiusYradius)
{
yRadius = xRadius;
CreatePoints();
}
}
}
#endif
}
The result is a drawn circle around the cube with the default color in pink :
The second script is also attached to the same cube with the drawn circle and animate colors to be rotating on the linerenderer circle :
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
[RequireComponent(typeof(LineRenderer))]
public class LineRendererColors : MonoBehaviour
{
public enum AnimationType { None, SingleColorMorph, MultiColorMorph, Shuffle, Shift };
public AnimationType myAnimationType;
LineRenderer myLineRenderer;
public float morphTime = 2;
void Start()
{
myLineRenderer = this.GetComponent<LineRenderer>();
switch (myAnimationType)
{
case AnimationType.SingleColorMorph:
StartCoroutine(RandomSingleColorMorphing(myLineRenderer, morphTime));
break;
case AnimationType.MultiColorMorph:
StartCoroutine(RandomMultiColorMorphing(myLineRenderer, morphTime));
break;
case AnimationType.Shuffle:
StartCoroutine(ShuffleGradient(myLineRenderer, .5f));
break;
case AnimationType.Shift:
StartCoroutine(AnimateLoop(myLineRenderer));
break;
}
}
void SetSingleColor(LineRenderer lineRendererToChange, Color newColor)
{
lineRendererToChange.startColor = newColor;
lineRendererToChange.endColor = newColor;
}
void SetSingleColor2(LineRenderer lineRendererToChange, Color newColor)
{
Gradient tempGradient = new Gradient();
GradientColorKey[] tempColorKeys = new GradientColorKey[2];
tempColorKeys[0] = new GradientColorKey(newColor, 0);
tempColorKeys[1] = new GradientColorKey(newColor, 1);
tempGradient.colorKeys = tempColorKeys;
lineRendererToChange.colorGradient = tempGradient;
}
void SetSingleColor3(LineRenderer lineRendererToChange, Color newColor)
{
Gradient tempGradient = lineRendererToChange.colorGradient;
GradientColorKey[] tempColorKeys = tempGradient.colorKeys;
for (int i = 0; i < tempColorKeys.Length; i++)
{
tempColorKeys[i].color = newColor;
}
tempGradient.colorKeys = tempColorKeys;
lineRendererToChange.colorGradient = tempGradient;
}
IEnumerator ShuffleGradient(LineRenderer targetLineRenderer, float waitTime)
{
while (true)
{
ShuffleGradient(targetLineRenderer);
yield return new WaitForSeconds(waitTime);
}
}
void ShuffleGradient(LineRenderer targetLineRenderer)
{
GradientColorKey[] newColorKeys = targetLineRenderer.colorGradient.colorKeys;
for (int i = 0; i < newColorKeys.Length; i++)
{
Color tempColor = newColorKeys[i].color;
int randomIndex = Random.Range(0, newColorKeys.Length - 1);
newColorKeys[i].color = newColorKeys[randomIndex].color;
newColorKeys[randomIndex].color = tempColor;
}
Gradient tempGradient = targetLineRenderer.colorGradient;
tempGradient.colorKeys = newColorKeys;
targetLineRenderer.colorGradient = tempGradient;
}
IEnumerator RandomMultiColorMorphing(LineRenderer lineRendererToChange, float timeToMorph)
{
float time = 0;
while (true)
{
GradientColorKey[] initialColorKeys = lineRendererToChange.colorGradient.colorKeys;
GradientColorKey[] newColorKeys = GenerateRandomColorKeys(initialColorKeys);
time = 0;
while (time < timeToMorph)
{
time += Time.deltaTime;
float progress = time / timeToMorph;
GradientColorKey[] currentColorKeys = GradientColorKeyLerp(initialColorKeys, newColorKeys, progress);
Gradient tempGradient = lineRendererToChange.colorGradient;
tempGradient.colorKeys = currentColorKeys;
lineRendererToChange.colorGradient = tempGradient;
yield return null;
}
yield return null;
}
}
GradientColorKey[] GradientColorKeyLerp(GradientColorKey[] initialColorKeys, GradientColorKey[] endColorKeys, float progress)
{
GradientColorKey[] newColorKeys = new GradientColorKey[initialColorKeys.Length];
for (int i = 0; i < newColorKeys.Length; i++)
{
newColorKeys[i].color = Color.Lerp(initialColorKeys[i].color, endColorKeys[i].color, progress);
newColorKeys[i].time = initialColorKeys[i].time;
}
return newColorKeys;
}
//assigns new color to each colorkey and uses Time from incomingColorKeys
GradientColorKey[] GenerateRandomColorKeys(GradientColorKey[] incomingColorKeys)
{
GradientColorKey[] newColorKeys = new GradientColorKey[incomingColorKeys.Length];
for (int i = 0; i < newColorKeys.Length; i++)
{
newColorKeys[i].color = RandomColor();
newColorKeys[i].time = incomingColorKeys[i].time;
}
return newColorKeys;
}
//asumes Single color, 2 colorkeys
IEnumerator RandomSingleColorMorphing(LineRenderer lineRendererToChange, float timeToMorph)
{
float time = 0;
Color initialColor = lineRendererToChange.colorGradient.colorKeys[0].color;
//this reduces colorkey amount to 2 just in case.
SetSingleColor2(lineRendererToChange, initialColor);
while (true)
{
initialColor = lineRendererToChange.colorGradient.colorKeys[0].color;
Color targetColor = RandomColor();
time = 0;
while (time < timeToMorph)
{
time += Time.deltaTime;
float progress = time / timeToMorph;
Color currentColor = Color.Lerp(initialColor, targetColor, progress);
SetSingleColor(lineRendererToChange, currentColor);
yield return null;
}
yield return null;
}
}
//Basically Color.Lerp?
Color ColorLerpMath(Color firstColor, Color secondColor, float progress)
{
Vector3 firstRGB = new Vector3(firstColor.r, firstColor.g, firstColor.b);
Vector3 secondRGB = new Vector3(secondColor.r, secondColor.g, secondColor.b);
Vector3 difference = secondRGB - firstRGB;
Vector3 lerpedRGB = firstRGB + (progress * difference);
return new Color(lerpedRGB.x, lerpedRGB.y, lerpedRGB.z);
}
Color RandomColor()
{
return new Color(Random.Range(0f, 1f), Random.Range(0f, 1f), Random.Range(0f, 1f));
}
//returns the gradient with a copy of the first key for intersection purposes.
Gradient AddInitialCopy(Gradient incomingGradient)
{
List<GradientColorKey> newColorKeys = new List<GradientColorKey>(incomingGradient.colorKeys);
Color interSectionColor = newColorKeys[0].color;
newColorKeys.Insert(0, new GradientColorKey(interSectionColor, 0));
Gradient newInitGradient = new Gradient();
newInitGradient.colorKeys = newColorKeys.ToArray();
return newInitGradient;
}
//remove first and last keys since they dont shift.
List<GradientColorKey> RemoveFirstAndLast(Gradient incomingGradient)
{
List<GradientColorKey> currentColorKeys = new List<GradientColorKey>(incomingGradient.colorKeys);
currentColorKeys.RemoveAt(currentColorKeys.Count - 1);
currentColorKeys.RemoveAt(0);
return currentColorKeys;
}
Color GetIntersectionColor(List<GradientColorKey> incomingKeys, int lowestIndex, int highestIndex)
{
Color firstColor = incomingKeys[lowestIndex].color;
Color lastColor = incomingKeys[highestIndex].color;
float distance = 1 - (incomingKeys[highestIndex].time - incomingKeys[lowestIndex].time);
float colorLerpAmount = (1f - incomingKeys[highestIndex].time) / distance; ;
Color newIntersectionColor = Color.Lerp(lastColor, firstColor, colorLerpAmount);
return newIntersectionColor;
}
//accepts max 7 colors, 1st and last should be at 0 and 1
IEnumerator AnimateLoop(LineRenderer lineRendererToChange, float movementPerTick = .001f)
{
lineRendererToChange.colorGradient = AddInitialCopy(lineRendererToChange.colorGradient);
while (true)
{
List<GradientColorKey> currentColorKeys = RemoveFirstAndLast(lineRendererToChange.colorGradient);
float highestTime = 0;
float lowestTime = 1;
int highestIndex = currentColorKeys.Count - 1;
int lowestIndex = 0;
//Move all inner ones.
for (int i = 0; i < currentColorKeys.Count; i++)
{
GradientColorKey tempColorKey = currentColorKeys[i];
float newTime = tempColorKey.time + movementPerTick;
if (newTime > 1)
{
newTime = newTime - 1;
}
tempColorKey.time = newTime;
currentColorKeys[i] = tempColorKey;
if (newTime < lowestTime)
{
lowestTime = newTime;
lowestIndex = i;
}
if (newTime > highestTime)
{
highestTime = newTime;
highestIndex = i;
}
}
Color newIntersectionColor = GetIntersectionColor(currentColorKeys, lowestIndex, highestIndex);
currentColorKeys.Insert(0, new GradientColorKey(newIntersectionColor, 0));
currentColorKeys.Add(new GradientColorKey(newIntersectionColor, 1));
Gradient tempGradient = lineRendererToChange.colorGradient;
tempGradient.colorKeys = currentColorKeys.ToArray();
lineRendererToChange.colorGradient = tempGradient;
yield return null;
}
}
void AssignGradient(LineRenderer targetLineRenderer, Gradient newGradient)
{
targetLineRenderer.colorGradient = newGradient;
}
void DrawTestLine()
{
Vector3 firstPos = new Vector3(-5, 0, 0);
Vector3 secondPos = new Vector3(5, 0, 0);
int resolution = 100;
myLineRenderer.positionCount = resolution;
myLineRenderer.SetPositions(MakeLine(firstPos, secondPos, 100));
}
//makes a line from point A to point B with resolution of size points
Vector3[] MakeLine(Vector3 initPos, Vector3 endPos, int points)
{
Vector3 difference = endPos - initPos;
Vector3[] newLine = new Vector3[points];
Vector3 differencePerPoint = difference / (float)(points - 1);
for (int i = 0; i < points; i++)
{
newLine[i] = initPos + (differencePerPoint * i);
}
return newLine;
}
}
When i'm running the game i see in the editor that the Color property of the linerenderer is changing in animation but the circle it self stay in the same default pink color and never change :
This is because the line renderer has no material. Add the appropriate material to your line renderer to solve the problem. If you want to add material through the code, you can do the following, but Unity usually makes changes to the shaders in each version, which may make it difficult to find the name of the shader.
if (!lineRenderer.material)
lineRenderer.material = new Material(Shader.Find("Sprites/Default"));

Drawing a Mandelbrot Set

I'm trying to make the function of the Mandelbrot Set, and I'm not sure what I'm doing wrong or right, here's the code:
private void StartCircles()
{
float savePower = BlackCircle.anchoredPosition.x;
GameObject[] AllCircles = new GameObject[itarations];
AllCircles[0] = BlackCircle.gameObject;
for (int i = 1; i < itarations; i++)
{
GameObject Circle = Instantiate(BlackCircle.gameObject, Vector3.zero, Quaternion.identity);
Circle.transform.SetParent(CanvasPerent);
savePower = Mathf.Pow(savePower, 2);
savePower += RedCircle.anchoredPosition.x;
Circle.GetComponent<RectTransform>().anchoredPosition = new Vector2(savePower,
AllCircles[i - 1].GetComponent<RectTransform>().anchoredPosition.y * -1);
AllCircles[i] = Circle;
}
CleanSqud = new GameObject[itarations];
CleanSqud = AllCircles;
}
I'm not sure what the y position should be and how could the x position be < 0 if it's a power of 2, it's automaticly > 0.
Here's the display:
i manged to get my code working after some time and i got some answars to share if anyone has my problen:
well i only wanted to make the function of the zn + 1 = zn * zn + c
i dident made the full set only this function, heres my code:
#region Actions
private void OnDestroy()
{
MoveBlack.HasMoved -= HasMoved;
MoveBlack.HasStoped -= HasStoped;
MoveRed.HasMoved -= HasMoved;
MoveRed.HasStoped -= HasStoped;
}
private void LateUpdate()
{
if (moved) { updateCircles(); }
if (hasparty)
{
foreach(GameObject game in CleanSqud)
{
game.GetComponent<Image>().color = new Color(Random.Range(0f, 1f), Random.Range(0f, 1f), Random.Range(0f, 1f));
}
}
}
private void HasMoved()
{
moved = true;
}
private void HasStoped()
{
moved = false;
}
#endregion
#region Updateing
private void updateCircles()
{
foreach (GameObject Circle in CleanSqud) { if (Circle.gameObject.name != "BlackCirlce") { Destroy(Circle); } }
StartCircles();
}
private void StartCircles()
{
float x = BlackCircle.anchoredPosition.x;
float y = BlackCircle.anchoredPosition.y;
GameObject[] AllCircles = new GameObject[itarations];
AllCircles[0] = BlackCircle.gameObject;
for (int i = 1; i < itarations; i++)
{
GameObject Circle = Instantiate(BlackCircle.gameObject, Vector3.zero, Quaternion.identity);
Circle.transform.SetParent(CanvasPerent);
AllCircles[i] = Circle;
x = Mathf.Pow(x, 2);
x -= Mathf.Pow(AllCircles[i - 1].GetComponent<RectTransform>().anchoredPosition.y, 2);
x += RedCircle.anchoredPosition.x;
y = (2 * AllCircles[i - 1].GetComponent<RectTransform>().anchoredPosition.x
* AllCircles[i - 1].GetComponent<RectTransform>().anchoredPosition.y) + RedCircle.anchoredPosition.y;
Circle.GetComponent<RectTransform>().anchoredPosition = new Vector2(x, y);
}
CleanSqud = new GameObject[itarations];
CleanSqud = AllCircles;
}
#endregion
so what you should do is instad of showing the y as a imaginary and the x as real show it using the equastion:
this x = power of the old x - power of the old y + c.x
this y = 2 * the old x * the old y + c.y
this should work!
thanks.

How can I add remove more number of objects at runtime?

The script is a bit long I'm not sure what I can reduce I will try.
When I'm running the program it's starting by making the square formation.
What I'm trying to archive is that at each time in runtime if I change the numberOfSquadMembers value up or down add/remove to each squad the more/less value of squad members.
Same for the numberOfSquads value.
The problem is I'm not sure how to do it in the Update() how to add more squads/squadmemebers or destroy when the values are less then the current ?
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using UnityEngine;
public class Formations : MonoBehaviour
{
enum Formation
{
Square, Circle, Triangle
}
[Header("Main Settings")]
[Space(5)]
[Range(4, 100)]
public int numberOfSquadMembers = 20;
[Range(1, 20)]
public int numberOfSquads = 1;
[Range(0, 4)]
public int columns = 4;
public int gaps = 10;
public int circleRadius = 10;
public float yOffset = 0;
[Range(3, 50)]
public float moveSpeed = 3;
[Range(3, 50)]
public float rotateSpeed = 1;
public float threshold = 0.1f;
public bool randomSpeed = false;
[Range(1, 100)]
public int randSpeedMin = 1;
[Range(1, 100)]
public int randSpeedMax = 1;
public bool startRandomFormation = false;
public string currentFormation;
private Formation formation;
private List<Quaternion> quaternions = new List<Quaternion>();
private List<Vector3> newpositions = new List<Vector3>();
private bool move = false;
private bool squareFormation = false;
private List<GameObject> squadMembers = new List<GameObject>();
private float[] step;
private int[] randomSpeeds;
private int index = 0;
private bool ready = false;
private GameObject[] squads;
private Vector3 startVector;
private Vector3 total;
private List<Vector3> endpositions = new List<Vector3>();
public void InitFormations()
{
if (startRandomFormation)
{
formation = (Formation)UnityEngine.Random.Range(0, Enum.GetNames(typeof(Formation)).Length);
}
else
{
formation = Formation.Square;
}
squads = GameObject.FindGameObjectsWithTag("Squad");
for (int i = 0; i < squads.Length; i++)
{
foreach (Transform squadMember in squads[i].transform)
{
squadMembers.Add(squadMember.gameObject);
}
}
foreach (GameObject unit in squadMembers)
{
if (unit != null)
{
total += unit.transform.position;
}
}
Vector3 center = total / squadMembers.Count;
//Vector3 endPos = GameObject.Find("Cube").transform.position;
foreach (GameObject unit in squadMembers)
{
startVector = unit.transform.position - center;
endpositions.Add(/*endPos + */startVector);
}
currentFormation = formation.ToString();
ChangeFormation();
randomSpeeds = RandomSpeeds(randSpeedMin, randSpeedMax, squadMembers.Count);
step = new float[squadMembers.Count];
ready = true;
}
private void Start()
{
InitFormations();
}
// Update is called once per frame
void Update()
{
if (ready == true)
{
if (Input.GetKeyDown(KeyCode.C))
{
randomSpeeds = RandomSpeeds(randSpeedMin, randSpeedMax, squadMembers.Count);
foreach (int speedV in randomSpeeds)
{
if (index == randomSpeeds.Length)
index = 0;
step[index] = speedV * Time.deltaTime;
index++;
}
ChangeFormation();
}
if (move == true)
{
MoveToNextFormation();
}
}
}
private void ChangeFormation()
{
switch (formation)
{
case Formation.Square:
FormationSquare();
break;
case Formation.Circle:
FormationCircle();
break;
case Formation.Triangle:
FormationTriangle();
break;
}
}
private void FormationTriangle()
{
newpositions = new List<Vector3>();
int height = Mathf.CeilToInt((Mathf.Sqrt(8 * squadMembers.Count + 1f) - 1f) / 2);
int slots = (int)(height * (height + 1f) / 2f);
float verticalModifier = 1.25f; // * 1.25f to increase horizontal space
float horizontalModifier = 0.8f; // * 0.8f to decrease "vertical" space
float width = 0.5f * (height - 1f);
Vector3 startPos = new Vector3(width * horizontalModifier, 0f, (float)(height - 1f) * verticalModifier);
int finalRowCount = height - slots + squadMembers.Count;
for (int rowNum = 0; rowNum < height && newpositions.Count < squadMembers.Count; rowNum++)
{
for (int i = 0; i < rowNum + 1 && newpositions.Count < squadMembers.Count; i++)
{
float xOffset = 0f;
if (rowNum + 1 == height)
{
// If we're in the last row, stretch it ...
if (finalRowCount != 1)
{
// Unless there's only one item in the last row.
// If that's the case, leave it centered.
xOffset = Mathf.Lerp(
rowNum / 2f,
-rowNum / 2f,
i / (finalRowCount - 1f)
) * horizontalModifier;
}
}
else
{
xOffset = (i - rowNum / 2f) * horizontalModifier;
}
float yOffset = (float)rowNum * verticalModifier;
Vector3 position = new Vector3(
startPos.x + xOffset, 0f, startPos.y - yOffset);
newpositions.Add(position);
}
}
move = true;
formation = Formation.Square;
}
private Vector3 FormationSquarePositionCalculation(int index) // call this func for all your objects
{
float posX = (index % columns) * gaps;
float posY = (index / columns) * gaps;
return new Vector3(posX, posY);
}
private void FormationSquare()
{
newpositions = new List<Vector3>();
quaternions = new List<Quaternion>();
for (int i = 0; i < squadMembers.Count; i++)
{
Vector3 pos = FormationSquarePositionCalculation(i);
//squadMembers[i].transform.position = new Vector3(transform.position.x + pos.x, 0, transform.position.y + pos.y);
squadMembers[i].transform.Rotate(new Vector3(0, -90, 0));
//newpositions.Add(new Vector3(transform.position.x + pos.x, 0, transform.position.y + pos.y));
newpositions.Add(new Vector3(endpositions[i].x + pos.x, 0, endpositions[i].y + pos.y));
}
move = true;
squareFormation = true;
formation = Formation.Circle;
}
private Vector3 FormationCirclePositionCalculation(Vector3 center, float radius, int index, float angleIncrement)
{
float ang = index * angleIncrement;
Vector3 pos;
pos.x = center.x + radius * Mathf.Sin(ang * Mathf.Deg2Rad);
pos.z = center.z + radius * Mathf.Cos(ang * Mathf.Deg2Rad);
pos.y = center.y;
return pos;
}
private void FormationCircle()
{
newpositions = new List<Vector3>();
quaternions = new List<Quaternion>();
Vector3 center = transform.position;
float radius = (float)circleRadius / 2;
float angleIncrement = 360 / squadMembers.Count;//(float)numberOfSquadMembers;
for (int i = 0; i < squadMembers.Count; i++)//numberOfSquadMembers; i++)
{
Vector3 pos = FormationCirclePositionCalculation(center, radius, i, angleIncrement);
var rot = Quaternion.LookRotation(center - pos);
if (Terrain.activeTerrain == true)
pos.y = Terrain.activeTerrain.SampleHeight(pos);
pos.y = pos.y + yOffset;
newpositions.Add(pos);
quaternions.Add(rot);
}
move = true;
squareFormation = false;
formation = Formation.Triangle;
}
private void MoveToNextFormation()
{
if (randomSpeed == false)
{
if (step.Length > 0)
step[0] = moveSpeed * Time.deltaTime;
}
for (int i = 0; i < squadMembers.Count; i++)
{
squadMembers[i].transform.LookAt(newpositions[i]);
if (randomSpeed == true)
{
squadMembers[i].transform.position =
Vector3.MoveTowards(squadMembers[i].transform.position, newpositions[i], step[i]);
}
else
{
squadMembers[i].transform.position =
Vector3.MoveTowards(squadMembers[i].transform.position, newpositions[i], step[0]);
}
if (Vector3.Distance(squadMembers[i].transform.position, newpositions[i]) < threshold)
{
if (squareFormation == true)
{
Vector3 degrees = new Vector3(0, 0, 0);
Quaternion quaternion = Quaternion.Euler(degrees);
squadMembers[i].transform.rotation = Quaternion.Slerp(squadMembers[i].transform.rotation, quaternion, rotateSpeed * Time.deltaTime);
}
else
{
squadMembers[i].transform.rotation = Quaternion.Slerp(squadMembers[i].transform.rotation, quaternions[i], rotateSpeed * Time.deltaTime);
}
}
}
}
private static int[] RandomSpeeds(int min, int max, int howMany)
{
int[] myNumbers = new int[howMany];
for (int i = 0; i < howMany; i++)
{
myNumbers[i] = UnityEngine.Random.Range(min, max);
}
return myNumbers;
}
}
This is the script that generate the squads and number of squads members first time :
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class SquadsGenerator : MonoBehaviour
{
public GameObject squadPrefab;
public int numberOfSquads;
public int numberOfMembersInsquad;
private GameObject squadsParent;
private void Start()
{
squadsParent = GameObject.Find("Squads");
GenerateSquads(numberOfSquads, numberOfMembersInsquad, squadPrefab);
}
// Update is called once per frame
void Update()
{
}
public void GenerateSquads(int squadsCount,
int numberOfMembers,
GameObject squadMemberPrefab)
{
for (int i = 0; i < squadsCount; i++)
{
GameObject newSquad = new GameObject();
newSquad.name = "Squad " + i;
newSquad.tag = "Squad";
newSquad.transform.parent = squadsParent.transform;
for (int x = 0; x < numberOfMembers; x++)
{
var go = Instantiate(squadMemberPrefab);
go.name = "Member " + x;
go.tag = "Squad Member";
go.transform.parent = newSquad.transform;
switch (i % 6)
{
case 0: ColorSquad(go, Color.green); break;
case 1: ColorSquad(go, Color.red); break;
case 2: ColorSquad(go, Color.blue); break;
case 3: ColorSquad(go, Color.yellow); break;
case 4: ColorSquad(go, Color.cyan); break;
case 5: ColorSquad(go, Color.magenta); break;
}
}
}
}
private void ColorSquad(GameObject squad, Color color)
{
Renderer rend = squad.GetComponent<Renderer>();
rend.material.SetColor("_Color", color);
}
}
Update :
What I have tried so far :
I added this part to the Update()
if(oldNumOfSquadMemebers != numberOfSquadMembers)
{
var tt = numberOfSquadMembers - oldNumOfSquadMemebers;
for (int i = 0; i < tt; i++)
{
var go = Instantiate(squadMemberPrefab);
squadMembers.Add(go);
}
oldNumOfSquadMemebers = numberOfSquadMembers;
FormationSquare();
}
The problem now is in the FormationSquare method :
private void FormationSquare()
{
newpositions = new List<Vector3>();
quaternions = new List<Quaternion>();
for (int i = 0; i < squadMembers.Count; i++)
{
Vector3 pos = FormationSquarePositionCalculation(i);
squadMembers[i].transform.Rotate(new Vector3(0, -90, 0));
newpositions.Add(new Vector3(endpositions[i].x + pos.x, 0, endpositions[i].y + pos.y));
}
move = true;
squareFormation = true;
formation = Formation.Circle;
}
Now I'm making a new instance for the newpositions List so each time I change the number of squad members there are more members but less newpositions so at some time I'm getting out of index exception.
If I don't make a new instance for the List then it will work but then after some time it will throw the out of index exception this time because it keep adding more and more new positions to the list.
So this newpositions List I'm stuck with it. Not sure how to solve it.

Sometimes entering an infinite loop in EPA algorithm when calculating contact normal and penetration depth

Currently I'm in the middle of developing a 2D physics engine by scratch. And Everything worked well by far. It's object oriented, obviously, with Vectors, matrices and shape classes written from scratch by myself. Collision are not yet that organized, but before that I need to make sure it works.
So for educational purposes I decided to implement GJK algorithm for collision detection and EPA for calculating contact normal. The GJK part works fine. And from the simplex (Triangle since it's 2D) I calculate the closest edge and from there I followed the algorithm. But for some reason which I can't seem to find, in some instances only, the program enters an infinite loop, adding new Vertices to the vertices list, but when I print them they're all the same, And the difference that should be near 0 in EPA gives big results. such as 70.55. So I hope you'll be able to help. Thank you in advance.
public class Collision
{
private Shape shape1;
private Shape shape2;
public List<Vector2> vertices;
private Vector2 direction;
private double TOLERENCE = 2;
public Vector2 contactNormal;
public double penetrationDepth;
private enum State
{
NO_INTERSECTION, FOUND_INTERSECTION, STILL_EVOLVING
}
private State result = State.STILL_EVOLVING;
public Collision(Shape shape1, Shape shape2)
{
this.shape1 = shape1;
this.shape2 = shape2;
vertices = new List<Vector2>();
}
public bool isColliding()
{
while (result == State.STILL_EVOLVING)
{
result = evolveSimplex();
}
if (result == State.FOUND_INTERSECTION)
findContactDetails();
return result == State.FOUND_INTERSECTION;
}
private State evolveSimplex()
{
if (vertices.Count == 0)
{
direction = shape1.position - shape2.position;
}
else if (vertices.Count == 1)
{
direction = -direction;
}
else if (vertices.Count == 2)
{
//ab is the vector from the 1st vertex to the second one
Vector2 ab = vertices[1] - vertices[0];
//a0 is the vector from the 1st vertex to the origin
Vector2 a0 = -vertices[0];
//Get the direction that is perpendicular to ab in the direction of the origin.
direction = ab.getPerpendicularUnitVectorTowards(a0);
}
else if (vertices.Count == 3)
{
//a,b and c are the three vertices
Vector2 a = vertices[0];
Vector2 b = vertices[1];
Vector2 c = vertices[2];
//lets find cb, ca and c0
Vector2 cb = b - c;
Vector2 ca = a - c;
Vector2 c0 = -c;
// let's find the perpendicular directions to cb and ca that are towards outside the simplex.
Vector2 cbPerp = cb.getPerpendicularUnitVectorTowards(c0);
Vector2 caPerp = ca.getPerpendicularUnitVectorTowards(c0);
if (cbPerp * c0 < 0)
{
vertices.Remove(a);
direction = cbPerp;
}
else if (caPerp * c0 < 0)
{
vertices.Remove(b);
direction = caPerp;
}
else
{
return State.FOUND_INTERSECTION;
}
}
return addSupport(direction) ? State.STILL_EVOLVING : State.NO_INTERSECTION;
}
private bool addSupport(Vector2 direction)
{
Vector2 support = shape1.getSupportPoint(direction) - shape2.getSupportPoint(-direction);
vertices.Add(support);
return direction * support > 0;
}
private void findContactDetails()
{
while (true)
{
Edge closestEdge = findClosestEdge(vertices);
Vector2 support = shape1.getSupportPoint(closestEdge.normal) - shape2.getSupportPoint(-closestEdge.normal);
double distance = support * closestEdge.normal;
Console.WriteLine("difference between the two = " + (Math.Abs(distance - closestEdge.distance)) + " and it's " + (Math.Abs(distance - closestEdge.distance) < TOLERENCE));
if (Math.Abs(distance - closestEdge.distance) < TOLERENCE)
{
Console.WriteLine("Contact details found");
contactNormal = closestEdge.normal;
penetrationDepth = distance;
break;
}
Console.WriteLine("That was not the closest edge. adding the support to the vertices at index " + closestEdge.index);
vertices.Insert(closestEdge.index, support);
Console.WriteLine("The number of vertices now = " + vertices.Count);
for (int i = 0; i < vertices.Count; i++)
{
Form1.dots.Add(vertices[i]);
}
}
}
private Edge findClosestEdge(List<Vector2> simplex)
{
Console.WriteLine("Finding closest edge");
Edge closest = new Edge();
closest.distance = Double.PositiveInfinity;
for (int i = 0; i < simplex.Count; i++)
{
Console.WriteLine("i = " + i);
int j = i + 1 == simplex.Count ? 0 : i + 1;
Vector2 a = simplex[i];
Vector2 b = simplex[j];
Vector2 edge = b - a;
Vector2 oa = a;
Vector2 normal = edge.getPerpendicularUnitVectorTowards(oa);
double d = Math.Abs(normal * oa);
Console.WriteLine("distance from origin to edge = " + d);
if (d < closest.distance)
{
closest.distance = d;
closest.normal = normal;
closest.index = j;
}
}
Console.WriteLine("returing closest edge " + closest.index + " distance = " + closest.distance);
return closest;
}
}
And Heres the vector2 class.
public class Vector2
{
public double x;
public double y;
public static Vector2 zeroVector2 = new Vector2(0,0);
public Vector2(double x, double y)
{
this.x = x;
this.y = y;
}
public Vector2 add(Vector2 vector)
{
return new Vector2(x + vector.x, y + vector.y);
}
public Vector2 subtract(Vector2 vector)
{
return new Vector2(x - vector.x, y - vector.y);
}
public double dot(Vector2 vector)
{
return x * vector.x + y * vector.y;
}
public Vector2 multiply(double scalar)
{
return new Vector2(x*scalar, y*scalar);
}
public double getMagnitude()
{
return Math.Sqrt(Math.Pow(x, 2) + Math.Pow(y, 2));
}
public void negate()
{
x = -x;
y = -y;
}
public Vector2 negated()
{
return new Vector2(-x, -y);
}
public bool isZeroVector()
{
if (x == 0 && y == 0)
return true;
else return false;
}
public Vector2 normalized()
{
return this * (1 / getMagnitude());
}
public void normalize()
{
x *= (1 / getMagnitude());
y *= (1 / getMagnitude());
}
public static Vector2 operator -(Vector2 vector)
{
return vector.negated();
}
public static Vector2 operator +(Vector2 v1, Vector2 v2)
{
return v1.add(v2);
}
public static Vector2 operator -(Vector2 v1, Vector2 v2)
{
return v1.subtract(v2);
}
public static double operator *(Vector2 v1, Vector2 v2)
{
return v1.dot(v2);
}
public static Vector2 operator *(Vector2 v, double scalar)
{
return v.multiply(scalar);
}
public static Vector2 operator *(double scalar, Vector2 v)
{
return v.multiply(scalar);
}
public static bool operator ==(Vector2 v1, Vector2 v2)
{
if (v1.x == v2.x && v1.y == v2.y)
return true;
return false;
}
public static bool operator !=(Vector2 v1, Vector2 v2)
{
if (v1.x == v2.x && v1.y == v2.y)
return false;
return true;
}
public override string ToString()
{
return "x = " + x + " , y = " + y;
}
public Vector3 to3DVector()
{
return new Vector3(x, y, 0);
}
public Matrix toMatrix()
{
return new Matrix(new double[2,1]{{x}, {y}});
}
public Vector2 rotate(double degrees)
{
return (Matrix.getRotationMatrix(degrees) * this.toMatrix()).toVector2();
}
public static Vector2 toVector2(Point point)
{
return new Vector2(point.X, point.Y);
}
public Point toPoint()
{
return new Point((int)x,(int)y);
}
public Vector2 getPerpendicularUnitVectorTowards(Vector2 direction)
{
Vector2 perp = new Vector2(-y, x) * (1 / getMagnitude());
Console.WriteLine(perp);
if (perp * direction < 0)
perp = -perp;
return perp;
}
}

XNA Finding mouse position with 2D camera

I have tried every way I found online to get the mouse position relative to the camera, but nothing will work. The selection tile always draws far away from the mouse. Also how would I only change the tile I am clicking on and not every tile with the same texture
Camera Class
public class Camera : Game1
{
protected float _zoom;
public Matrix _transform;
public Vector2 _pos;
protected float _rotation;
public Camera()
{
_zoom = 1.0f;
_rotation = 0.0f;
_pos = Vector2.Zero;
}
public float Zoom
{
get { return _zoom; }
set { _zoom = value; if (_zoom < 0.1f) _zoom = 0.1f; } // Negative zoom will flip image
}
public float Rotation
{
get { return _rotation; }
set { _rotation = value; }
}
public void Move(Vector2 amount)
{
_pos += amount;
}
public Vector2 Pos
{
get { return _pos; }
set { _pos = value; }
}
public Matrix get_transformation()
{
_transform =
Matrix.CreateTranslation(new Vector3(-_pos.X, -_pos.Y, 0)) *
Matrix.CreateRotationZ(Rotation) *
Matrix.CreateScale(_zoom) *
Matrix.CreateTranslation(new Vector3(1024 * 0.5f, 768 * 0.5f, 0));
return _transform;
}
public void Update()
{
Input();
}
protected virtual void Input()
{
KeyboardState _keyState;
_keyState = Keyboard.GetState();
if (_keyState.IsKeyDown(Keys.A))
{
_pos.X -= 5f;
}
if (_keyState.IsKeyDown(Keys.D))
{
_pos.X += 5f;
}
if (_keyState.IsKeyDown(Keys.W))
{
_pos.Y -= 5f;
}
if (_keyState.IsKeyDown(Keys.S))
{
_pos.Y += 5f;
}
}
}
Tile Class
class TileGeneration
{
public Block[] tiles = new Block[3];
public int width, height;
public int[,] index;
public Texture2D grass, dirt, selection;
bool selected;
MouseState MS;
Vector2 mousePos;
Camera camera;
public TileGeneration()
{
}
public void Load(ContentManager content, GraphicsDevice g)
{
grass = content.Load<Texture2D>(#"Tiles/grass");
dirt = content.Load<Texture2D>(#"Tiles/dirt");
selection = content.Load<Texture2D>(#"Tiles/selection");
tiles[0] = new Block { Type = BlockType.Grass, Position = Vector2.Zero, texture = grass};
tiles[1] = new Block { Type = BlockType.Dirt, Position = Vector2.Zero, texture = dirt};
width = 50;
height = 50;
index = new int[width, height];
camera = new Camera();
Random rand = new Random();
for (int x = 0; x < width; x++)
{
for (int y = 0; y < height; y++)
{
index[x,y] = rand.Next(0,2);
}
}
}
public void Update()
{
MS = Mouse.GetState();
Matrix inverseViewMatrix = Matrix.Invert(camera.get_transformation());
Vector2 mousePosition = new Vector2(Mouse.GetState().X, Mouse.GetState().Y);
Vector2 worldMousePosition = Vector2.Transform(mousePosition, inverseViewMatrix);
mousePos = worldMousePosition;
Console.WriteLine(mousePos);
if (MS.LeftButton == ButtonState.Pressed)
{
Console.WriteLine("Selected");
selected = true;
}
}
public void Draw(SpriteBatch spriteBatch)
{
for (int x = 0; x < width; x++)
{
for (int y = 0; y < height; y++)
{
spriteBatch.Draw(tiles[index[x,y]].texture, new Rectangle(x * 64, y * 64, 64, 64),
Color.White);
if (selected && IsMouseInsideTile(x, y))
{
if (tiles[index[x,y]].texture == grass)
tiles[index[x,y]].texture = dirt;
}
if(IsMouseInsideTile(x, y))
spriteBatch.Draw(selection, new Rectangle(x * 64, y * 64, 64, 64), Color.White);
}
}
}
public bool IsMouseInsideTile(int x, int y)
{
return (mousePos.X >= x * 64 && mousePos.X <= (x + 1) * 64 &&
mousePos.Y >= y * 64 && mousePos.Y <= (y + 1) * 64);
}
Game1 Draw
protected override void Draw(GameTime gameTime)
{
GraphicsDevice.Clear(Color.CornflowerBlue);
spriteBatch.Begin(SpriteSortMode.Immediate, BlendState.AlphaBlend, null, null, null, null,
camera.get_transformation());
tile.Draw(this.spriteBatch);
player.Draw(this.spriteBatch);
spriteBatch.End();
base.Draw(gameTime);
}
There may be a better way, but:
// absoluteMouseX will be the value from your MouseState, and camera will be an instance of your class
// You may need to convert your rotation to radians.
float relativeMouseX = absoluteMouseX + camera.Pos.X;
float relativeMouseY = absoluteMouseY + camera.Pos.Y;
I don't have XNA right now to test it, but I recall having problems with this.
A snippet of code I have which may help:
Vector2 mouse = new Vector2(ms.X, ms.Y);
Matrix transform = Matrix.Invert(camera.ViewMatrix);
Vector2.Transform(ref mouse, ref transform, out mouse);
selectedRow = (int)(mouse.Y / Tile.SIZE);
if (selectedRow < 0) selectedRow = 0;
else if (selectedRow >= rows) selectedRow = rows - 1;
selectedCol = (int)(mouse.X / Tile.SIZE);
if (selectedCol < 0) selectedCol = 0;
else if (selectedCol >= cols) selectedCol = cols - 1;
Two things:
1. Notice how the row depends on the Y component of the Mouse, and the column on the X component.
2. Notice that it's considerably faster to directly get which tile the Mouse lays on rather than asking every single tile if it has the Mouse "inside".

Categories