I am doing a Tank Trouble (aka. AZ) like game in c#, winforms application.
I am drawing to a picturebox, the walls are stored in a list, each wall has a specified thickness, a starting and ending point and a boolean which tells if the wall is vertical (true), or if it is horizontal (the bool is false)
My collision detector goes trough the list and checks if it collides with one of them, then sets the respective booleans if collision was detected in a direction (up, down, left, right)
My problem is, that sometimes there are false positives, and false negatives too, there are cases, when it stucks while no walls are near, sometimes goes through the walls.
Could somebody give some suggestions how to improve it?
Is there a more optimal way to do it?
Here is my code
public void MoveTank()
{
// Calculate velocity from angle and base speed
Vx = (float)(moveSpeed * Math.Sin((angle * 0.0174532925)));
Vy = (float)(moveSpeed * Math.Cos((angle * 0.0174532925)));
CollisionDetector();
if (up == true)
{
Vy = 0;
}
if (down == true)
{
Vy = 0;
}
if (left == true)
{
Vx = 0;
}
if (right == true)
{
Vx = 0;
}
up = false;
down = false;
left = false;
right = false;
if (tUp) //if up key is pressed
{
tankCo.Y -= Vy;
tankCo.X += Vx;
}
if (tDown) //if down key is pressed
{
tankCo.Y += Vy;
tankCo.X -= Vx;
}
if (tLeft) //rotate left
{
angle -= angV;
if (angle < 0)
{
angle = 360 + angle;
}
angle = angle % 360;
img = RotateImage(OldImg, angle);
}
if (tRight) //rotate right
{
angle += angV;
angle = angle % 360;
if (angle < 0)
{
angle = 360 + angle;
}
img = RotateImage(OldImg, angle);
}
}
public void CollisionDetector()
{
foreach (Walls.Brick w in GameWindow.wall.allWalls)
{
if(w.vertical == true)
{
if (this.tankCo.X > w.wallStart.X && this.tankCo.X + this.Vx < w.wallStart.X)
{
this.left = true;
}
else if (this.tankCo.X < w.wallStart.X && this.tankCo.X + this.Vx > w.wallStart.X)
{
this.right = true;
}
}
else if(w.vertical == false)
{
if (this.tankCo.Y > w.wallStart.Y && this.tankCo.Y + this.Vy < w.wallStart.Y)
{
this.up = true;
}
else if (this.tankCo.Y < w.wallStart.Y && this.tankCo.Y + this.Vy > w.wallStart.Y)
{
this.down = true;
}
}
}
}
if it isn't enough here is my entire repository
https://github.com/SorbanElod/CSharp/tree/main/SPANzer
After some work here is what i came up with
Thank you for your help
public void MoveTank()
{
if(tUp == tDown)
{
Vx = 0;
Vy = 0;
}
else if (tUp)
{
// Calculate velocity from angle and base speed
Vx = (float)(moveSpeed * Math.Sin((angle * 0.0174532925))); //that ugly number is PI/180
Vy = -(float)(moveSpeed * Math.Cos((angle * 0.0174532925)));
}
else if (tDown)
{
// Calculate velocity from angle and base speed
Vx = -(float)(moveSpeed * Math.Sin((angle * 0.0174532925)));
Vy = (float)(moveSpeed * Math.Cos((angle * 0.0174532925)));
}
//If it collides then one component of the velocity will be removed (Vx or Vy)
CollisionDetector();
tankCo.X += Vx;
tankCo.Y += Vy;
if (tLeft)
{
angle -= angV;
if (angle < 0)
{
angle = 360 + angle;
}
angle = angle % 360;
img = RotateImage(OldImg, angle);
}
if (tRight)
{
angle += angV;
angle = angle % 360;
if (angle < 0)
{
angle = 360 + angle;
}
img = RotateImage(OldImg, angle);
}
}
public void CollisionDetector()
{
foreach (Walls.Brick w in GameWindow.wall.allWalls)
{
if(w.vertical == false) // horizontal wall
{
//check if tank can collide with wall's side
if (tankCo.X >= w.wallStart.X && tankCo.X <= w.wallEnd.X)
{
//collision with tehe wall above
if (tankCo.Y > w.wallStart.Y && tankCo.Y + Vy < w.wallStart.Y)
{
Vy = 0;
}
//collision with the wall below
if (tankCo.Y + imgSize < w.wallStart.Y && tankCo.Y + imgSize + Vy > w.wallStart.Y)
{
Vy = 0;
}
}
//checks if tank can collide with the walls end
if (tankCo.Y < w.wallStart.Y && tankCo.Y + imgSize > w.wallStart.Y)
{
//collision with the right end
if (tankCo.X > w.wallEnd.X && tankCo.X + Vx < w.wallEnd.X)
{
Vx = 0;
}
//collision with the left end
if (tankCo.X + imgSize < w.wallStart.X && tankCo.X + imgSize + Vx > w.wallStart.X)
{
Vx = 0;
}
}
}
else if(w.vertical == true) //vertical
{
//check if tank can collide with wall's side
if (tankCo.Y >= w.wallStart.Y && tankCo.Y <= w.wallEnd.Y)
{
//collision with the wall from right
if (tankCo.X > w.wallStart.X && tankCo.X + Vx < w.wallStart.X)
{
Vx = 0;
}
//collision with the wall from left
if (tankCo.X + imgSize < w.wallStart.X && tankCo.X + imgSize + Vx > w.wallStart.X)
{
Vx = 0;
}
}
//checks if tank can collide with the walls end
if (tankCo.X < w.wallStart.X && tankCo.X + imgSize > w.wallStart.X)
{
//collision with the bottom end
if (tankCo.Y > w.wallEnd.Y && tankCo.Y + Vy < w.wallEnd.Y)
{
Vy = 0;
}
//collision with top end
if (tankCo.Y + imgSize < w.wallStart.Y && tankCo.Y + imgSize + Vy > w.wallStart.Y)
{
Vy = 0;
}
}
}
}
}
It may be a bit complicated but it works for me
Related
Canvas
map_content is a large map that is only partially visible on the screen, it can be moved by "x" and "y"
a script has been added to map_content to change the scale depending on the infringement of the screen with two fingers.
However, the whole pain is that when you zoom in on the map, the VISIBLE AREA of the MAP gets closer to the center.
This is due to the fact that in addition to scale, you need to change the position of map_content. But I have no idea how to calculate the right value to change
using UnityEngine;
public class PinchToChangeMap : MonoBehaviour
{
[SerializeField] private float minScale, maxScale;
float initialFingersDistance;
Vector3 initialScale;
void Update()
{
if (Input.touches.Length == 2)
{
Touch t1 = Input.touches[0];
Touch t2 = Input.touches[1];
if (t1.phase == TouchPhase.Began || t2.phase == TouchPhase.Began)
{
initialFingersDistance = Vector2.Distance(t1.position, t2.position);
initialScale = gameObject.transform.localScale;
}
else if (t1.phase == TouchPhase.Moved || t2.phase == TouchPhase.Moved)
{
var currentFingersDistance = Vector2.Distance(t1.position, t2.position);
var scaleFactor = currentFingersDistance / initialFingersDistance;
if (gameObject.transform.localScale.x <= maxScale && gameObject.transform.localScale.x >= minScale)
{
gameObject.transform.localScale = initialScale * scaleFactor;
//gameObject.transform.position = new Vector2(initialScale.x / gameObject.transform.position.x * gameObject.transform.localScale.x, initialScale.y / gameObject.transform.position.y * gameObject.transform.localScale.y);
Debug.Log("Scale: " + gameObject.transform.localScale + " | Position: " + gameObject.transform.position + " | Scale: " + scaleFactor);
}
if(gameObject.transform.localScale.x > maxScale)
{
gameObject.transform.localScale = new Vector3(maxScale, maxScale, 1);
}else if (gameObject.transform.localScale.x < minScale)
{
gameObject.transform.localScale = new Vector3(minScale, minScale, 1);
}
}
}
}
}
So I am making a 2D space shmup that handles combat in a naval way. So you shoot out the broadsides of the ship, your shields and hull are divided into 4 sections: Forward, Starboard, Port, and Rear. I am not the greatest with math, but I managed to find a script that detects the side of my polygon collider that was hit by say a collision or projectile. That all works great.
The problem is my sprite rotates to steer in 2D space. So when I collide with something say for example with the nose of my ship, if my ship's nose is up then the collision is detected properly. But if the ship is rotated and the nose is now on the left and I collide with something on the nose, the script will detect the nose collision as a port side collision instead. Could somebody help me with correcting the math to account for my ship's rotation?
Collision2DExtension.cs
using UnityEngine;
namespace PixelsoftGames
{
public static class Collision2DExtensions
{
public static Collision2DSideType GetContactSide(Vector2 max, Vector2 center, Vector2 contact)
{
Collision2DSideType side = Collision2DSideType.None;
float diagonalAngle = Mathf.Atan2(max.y - center.y, max.x - center.x) * 180 / Mathf.PI;
float contactAngle = Mathf.Atan2(contact.y - center.y, contact.x - center.x) * 180 / Mathf.PI;
if (contactAngle < 0)
{
contactAngle = 360 + contactAngle;
}
if (diagonalAngle < 0)
{
diagonalAngle = 360 + diagonalAngle;
}
if (
((contactAngle >= 360 - diagonalAngle) && (contactAngle <= 360)) ||
((contactAngle <= diagonalAngle) && (contactAngle >= 0))
)
{
side = Collision2DSideType.Starboard;
}
else if (
((contactAngle >= 180 - diagonalAngle) && (contactAngle <= 180)) ||
((contactAngle >= 180) && (contactAngle <= 180 + diagonalAngle))
)
{
side = Collision2DSideType.Port;
}
else if (
((contactAngle >= diagonalAngle) && (contactAngle <= 90)) ||
((contactAngle >= 90) && (contactAngle <= 180 - diagonalAngle))
)
{
side = Collision2DSideType.Forward;
}
else if (
((contactAngle >= 180 + diagonalAngle) && (contactAngle <= 270)) ||
((contactAngle >= 270) && (contactAngle <= 360 - diagonalAngle))
)
{
side = Collision2DSideType.Rear;
}
return side.Opposite();
}
static bool ranOnce = false;
public static Collision2DSideType GetContactSide(this Collision2D collision)
{
Vector2 max = collision.collider.bounds.max;
Vector2 center = collision.collider.bounds.center;
Vector2 contact = collision.GetContact(0).point;
if (!ranOnce)
{
ranOnce = true;
Debug.Log("Max: " + max);
Debug.Log("Center: " + center);
Debug.Log("Contact: " + contact);
}
return GetContactSide(max, center, contact);
}
}
}
Collision2DSideTypeExtensions.cs
namespace PixelsoftGames
{
public static class Collision2DSideTypeExtensions
{
public static Collision2DSideType Opposite(this Collision2DSideType sideType)
{
Collision2DSideType opposite;
if (sideType == Collision2DSideType.Port)
{
opposite = Collision2DSideType.Starboard;
}
else if (sideType == Collision2DSideType.Starboard)
{
opposite = Collision2DSideType.Port;
}
else if (sideType == Collision2DSideType.Forward)
{
opposite = Collision2DSideType.Rear;
}
else if (sideType == Collision2DSideType.Rear)
{
opposite = Collision2DSideType.Forward;
}
else
{
opposite = Collision2DSideType.None;
}
return opposite;
}
}
}
Collision2DSideType
public enum Collision2DSideType { None, Port, Starboard, Forward, Rear }
In the method GetContactSide you never get the rotation of your sprite, it is like your sprite angle is always 0
One solution for this is to add as a parameter the angle of your sprite to the method and add that angle to the the condition to determine wich side of the sprite it is
It can look like that :
public static class Collision2DExtensions
{
public static Collision2DSideType GetContactSide(Vector2 max, Vector2 center, Vector2 contact, float angle)
{
...
if (
((contactAngle >= (360 - diagonalAngle) + angle) && (contactAngle <= 360 + angle)) ||
((contactAngle <= diagonalAngle + angle) && (contactAngle >= 0 + angle))
)
...
`
I think you should do that for each of these conditions
I am a making an endless skiing game, and already have a scoring system, however i want to add a score multiplier based on the number of consecutive tricks done without touching the ground. here's my script so far:
public class tricksScore : MonoBehaviour
{
private float flips = 0;
private float deltaRotation = 0;
private float currentRotation = 0;
private float WindupRotation = 0;
public static Rigidbody2D rigbod;
public Text scores;
private int trickscore;
private int iflip;
private int oldscore;
private int incInScore;
public float speed;
private float counter;
private int flipscore;
private int rockDestroy;
private bool grounded;
private int multiplier = 1;
// Collision2D coll;
// Start is called before the first frame update
void Start()
{
speed = GameObject.FindGameObjectWithTag("Player").GetComponent<PlayerController>().speed;
scores = GameObject.Find("score").GetComponent<Text>();
rigbod = GameObject.FindGameObjectWithTag("Player").GetComponent<Rigidbody2D>();
grounded = GameObject.FindGameObjectWithTag("Player").GetComponent<PlayerController>().grounded;
}
// Update is called once per frame
void Update()
{
rigbod.velocity = new Vector2(speed, rigbod.velocity.y);
deltaRotation = currentRotation - rigbod.transform.eulerAngles.z;
currentRotation = rigbod.transform.eulerAngles.z;
if (deltaRotation >= 300) deltaRotation -= 360;
if (deltaRotation <= -300) deltaRotation += 360;
WindupRotation += (deltaRotation);
flips = WindupRotation / 340;
iflip = (int)flips;
iflip = iflip * -1;
flipscore = iflip * 10;
trickscore = flipscore + rockDestroy;
scores.text = "score " + (trickscore * multiplier);
incInScore = trickscore - oldscore;
if (incInScore >= 10)
{
oldscore = trickscore;
}
//speed += (Mathf.Round(incInScore)) / 100.0f;
if (incInScore > 1 && incInScore <= 10)
{
speed = speed + 0.15f;
counter += 3f;
}
if (incInScore > 10 && incInScore <= 20)
{
speed = speed + 0.25f;
counter += 3f;
}
if (incInScore > 20 && incInScore <= 50)
{
speed = speed + 0.50f;
counter += 3f;
}
if (incInScore > 50 && incInScore <= 100)
{
speed = speed + 0.75f;
counter += 3f;
}
if (incInScore > 100 && incInScore <= 200)
{
speed = speed + 1f;
counter += 3.5f;
}
if (incInScore > 200)
{
speed = speed + 2f;
counter += 4f;
}
if (incInScore > 5 && grounded == false)
{
multiplier = multiplier + 1;
}
else if (grounded == true)
{
multiplier = 1;
}
if (speed > 5.15f)
{
speed -= 0.05f * Time.deltaTime;
}
else if (speed == 5.15f)
{
speed = 5.15f;
}
counter -= 1.0f * Time.deltaTime;
if (counter < 0)
{
counter = 0;
}
if (incInScore >= 10)
{
incInScore = 0;
}
if (incInScore < 0)
{
incInScore = incInScore * -1;
}
}
private void OnCollisionEnter2D(Collision2D coll)
{
//counter = GameObject.FindGameObjectWithTag("Player").GetComponent<tricksScore>().counter;
if (counter > 0)
{
if (coll.collider.tag == "rock")
{
Destroy(coll.gameObject);
speed = speed + 0.15f;
rockDestroy = rockDestroy + 5;
counter = counter + 2f;
}
}
}
}
I know the scripts dirty, but hopefully its still comprehensible for you all. thanks in advance for the help.
Your ground boolean is only set in start, so it will almost always be false. Add the check statement in the update function, as when you set a variable with this property, it will only take the value at that time, it will not automatically update.
I have use 3D level screen when I swipe on screen than my 3D model rotate left or right and swipe speed increase object rotation speed increase when I swipe ended then automatic decrease rotation speed object speed increase that not descrease and my object rotate continue.
How that maintain with Unity C# code:
public class Rotate: MonoBehaviour {
public Transform fan;
private bool flagLeft = false;
private bool flagRight = false;
private float speedLeft;
private float speedRight;
private bool CheckRight = true;
private bool CheckLeft = true;
Vector2 firstPressPos;
Vector2 secondPressPos;
Vector2 currentSwipe;
void Start() {
speedLeft = 1;
speedRight = 1;
}
void Update() {
Swipe();
if (flagLeft) {
fan.transform.Rotate(0, speedLeft * Time.deltaTime, 0);
}
if (flagRight) {
fan.transform.Rotate(0, -speedRight * Time.deltaTime, 0);
}
}
public void Swipe() {
if (Input.touches.Length > 0) {
Touch t = Input.GetTouch(0);
if (t.phase == TouchPhase.Began) {
//save began touch 2d point
firstPressPos = new Vector2(t.position.x, t.position.y);
}
if (t.phase == TouchPhase.Ended) {
//save ended touch 2d point
secondPressPos = new Vector2(t.position.x, t.position.y);
//create vector from the two points
currentSwipe = new Vector3(secondPressPos.x - firstPressPos.x, secondPressPos.y - firstPressPos.y);
//normalize the 2d vector
currentSwipe.Normalize();
//swipe upwards
if (currentSwipe.y > 0 && currentSwipe.x > -0.5 f && currentSwipe.x < 0.5 f) {
Debug.Log("up swipe");
}
//swipe down
if (currentSwipe.y < 0 && currentSwipe.x > -0.5 f && currentSwipe.x < 0.5 f) {
Debug.Log("down swipe");
}
//swipe left
if (currentSwipe.x < 0 && currentSwipe.y > -0.5 f && currentSwipe.y < 0.5 f) {
flagLeft = true;
if (Debug.isDebugBuild) {
Debug.Log("Swipe Left");
}
if (CheckLeft) {
speedLeft = speedRight;
CheckLeft = false;
CheckRight = true;
}
speedLeft += 10;
Debug.Log("Left Swipe Speed=" + speedLeft);
}
//swipe right
if (currentSwipe.x > 0 && currentSwipe.y > -0.5 f && currentSwipe.y < 0.5 f) {
flagRight = true;
Debug.Log("Swipe Right");
if (CheckRight) {
speedRight = speedLeft;
CheckRight = false;
CheckLeft = true;
}
speedRight += 10;
Debug.Log("Right Swipe Speed=" + speedRight);
}
}
}
}
}
I'm learning XNA right now and i'm pretty new to programming in general. I'm making a platformer now but when I walk into a platform from left or right I get teleported to the top of the platform. The collision is only working with the last platform added to the platform list.
This is in Game class:
LoadContent:
for (int i = 0; i < 3; i++)
{
platform0List.Add(new Platform0(new Vector2(70 + (i * 300), 400), platform0Texture));
}
Update:
protected override void Update(GameTime gameTime)
{
keyboard = Keyboard.GetState();
player.Update(keyboard, keyboardPrev, platform0List);
foreach (Platform0 platform in platform0List)
{
if (!Move_Free(player.position.X, player.position.Y + player.gravity, player.texture.Width, player.texture.Height, platform.rectangle) && player.gravity >= 0)
{
player.ground = true;
if (player.position.Y + player.texture.Height + player.gravity > platform.position.Y)
{
player.position.Y = platform.position.Y - player.texture.Height;
}
else if (player.position.Y + player.texture.Height + player.gravity < platform.position.Y)
{
player.gravity = platform.position.Y - player.texture.Height;
}
break;
}
else
{
player.ground = false;
}
}
if (keyboard.IsKeyDown(Keys.Escape)) this.Exit();
keyboardPrev = keyboard;
base.Update(gameTime);
}
This is my Move_Free method
public static bool Move_Free(float x, float y, int width, int height, Rectangle rectangle)
{
Rectangle rect = new Rectangle((int)x,(int)y,width,height);
if(rect.Intersects(rectangle))
{
return false;
}
else
{
return true;
}
}
This is in Player class
foreach (Platform0 platform in platform0List)
{
if (keyboard.IsKeyDown(Keys.Right) && Game1.Move_Free(position.X + 5, position.Y, texture.Width, texture.Height, platform.rectangle))
{
moveRight = true;
}
else if (keyboard.IsKeyDown(Keys.Right) && ((position.X + texture.Width - platform.position.X) * -1) > 0)
{
position.X += (position.X + texture.Width - platform.position.X) * -1;
}
else
{
moveRight = false;
}
if (keyboard.IsKeyDown(Keys.Left) && Game1.Move_Free(position.X - 5, position.Y, texture.Width, texture.Height, platform.rectangle))
{
moveLeft = true;
}
else if (keyboard.IsKeyDown(Keys.Left) && position.X - (platform.position.X + platform.texture.Width) > 0)
{
position.X -= position.X - (platform.position.X + platform.texture.Width);
}
else
{
moveLeft = false;
}
}
if (moveRight) position.X += 5;
if (moveLeft) position.X -= 5;
if (keyboard.IsKeyDown(Keys.Up) && ground)
{
gravity = -10;
ground = false;
}
if(ground)
{
gravity = 0;
}
else
{
gravity += 9.8f / 60f;
position.Y += gravity;
}
Each iteration of your forloop overwrites your moveLeft and moveRight variables.
Therefore, only the last platform values will remain.