C# Collision test of a ship and asteriod [closed] - c#

This question is unlikely to help any future visitors; it is only relevant to a small geographic area, a specific moment in time, or an extraordinarily narrow situation that is not generally applicable to the worldwide audience of the internet. For help making this question more broadly applicable, visit the help center.
Closed 10 years ago.
We are trying to to do a collision detection for the ship and asteroid.
If success than it should detect the collision before N turns.
However it is confused between angle 350 and 15 and it is not really working.
Sometimes it is moving but sometime it is not moving at all.
On the other hand, it is not shooting at the right time as well.
I just want to ask how to make the collision detection working???
And how to solve the angle confusion problem?
// Get velocities of asteroid
Console.WriteLine("lol");
// IF equation is between -2 and -3
if (equation1a <= -2)
{
// Calculate no. turns till asteroid hits
float turns_till_hit = dx / vx;
// Calculate angle of asteroid
float asteroid_angle_rad = (float)Math.Atan(Math.Abs(dy / dx));
float asteroid_angle_deg = (float)(asteroid_angle_rad * 180 / Math.PI);
float asteroid_angle = 0;
// Calculate angle if asteroid is in certain positions
if (asteroid.Y > ship.Y && asteroid.X > ship.X)
{
asteroid_angle = asteroid_angle_deg;
}
else if (asteroid.Y < ship.Y && asteroid.X > ship.X)
{
asteroid_angle = (360 - asteroid_angle_deg);
}
else if (asteroid.Y < ship.Y && asteroid.X < ship.X)
{
asteroid_angle = (180 + asteroid_angle_deg);
}
else if (asteroid.Y > ship.Y && asteroid.X < ship.X)
{
asteroid_angle = (180 - asteroid_angle_deg);
}
// IF turns till asteroid hits are less than 35
if (turns_till_hit < 50)
{
float angle_between = 0;
// Calculate angle between if asteroid is in certain positions
if (asteroid.Y > ship.Y && asteroid.X > ship.X)
{
angle_between = ship_angle - asteroid_angle;
}
else if (asteroid.Y < ship.Y && asteroid.X > ship.X)
{
angle_between = (360 - Math.Abs(ship_angle - asteroid_angle));
}
else if (asteroid.Y < ship.Y && asteroid.X < ship.X)
{
angle_between = ship_angle - asteroid_angle;
}
else if (asteroid.Y > ship.Y && asteroid.X < ship.X)
{
angle_between = ship_angle - asteroid_angle;
}
// If angle less than 0, add 360
if (angle_between < 0)
{
//angle_between %= 360;
angle_between = Math.Abs(angle_between);
}
// Calculate no. of turns to face asteroid
float turns_to_face = angle_between / 25;
if (turns_to_face < turns_till_hit)
{
float ship_angle_left = ShipAngle(ship_angle, "leftKey", 1);
float ship_angle_right = ShipAngle(ship_angle, "rightKey", 1);
float angle_between_left = Math.Abs(ship_angle_left - asteroid_angle);
float angle_between_right = Math.Abs(ship_angle_right - asteroid_angle);
if (angle_between_left < angle_between_right)
{
leftKey = true;
}
else if (angle_between_right < angle_between_left)
{
rightKey = true;
}
}
if (angle_between > 0 && angle_between < 25)
{
spaceKey = true;
}
}
}
}
}

How to make the collision detection working?
Find a reproducible scenario that does not work correctly.
Work out by hand the correct calculations for that scenario.
Start your program in the debugger. Watch the program perform those calculations.
When you reach a calculation that does not match the correct calculation that you worked out by hand, that's where the bug is.
In particular, look for places where the comments do not match the code; those are likely to be wrong. For example, this bit:
// If angle less than 0, add 360
if (angle_between < 0)
{
angle_between = Math.Abs(angle_between);
}
The comment says one thing and the code does something completely different. It is the code that actually runs.
And a word of advice: do all your calculations in doubles. There is no reason to keep casting doubles down to floats. Your making your program slower and less accurate by doing so. Also consider doing all your calculations in radians rather than converting back and forth between radians and degrees.
Also I note that you have plenty of opportunities for divisions by zero in your code. Those are likely to be bugs.

Related

How to use binary search on an animation curve to find time given value?

I'm using Unity's animation curves which unfortunately only have an Evaluate function that given time gives the value. I want to create a method that does the opposite of that. value gives time. I looked at this post: Unity get time by value but all the answers refer to inverting the curve which I think, and most comments agree, is cumbersome and error prone. I figured a better solution would be to use binary search. However, my current implementation sometimes fails to find the time.
For context the curve represents the velocity of a car as it accelerates. It is always monotonic increasing. So far this is what I've got:
float GetTimeOnCurve(AnimationCurve c, float speed)
{
if (speed < c.keys[0].value)
{
return c.keys[0].time;
}else if (speed > c.keys.Last().value)
{
return c.keys.Last().time;
}
float min = c.keys[0].time;
float max = c.keys.Last().time;
const float elipson = 0.001f;
while (min <= max)
{
float mid = (min + max) / 2;
if (speed >= c.Evaluate(mid) - elipson && speed <= c.Evaluate(mid) + elipson)
{
return mid;
}else if (speed < c.Evaluate(mid))
{
max = mid - elipson;
}
else
{
min = mid + elipson;
}
}
Debug.LogError("Speed not found");
return TopSpeed;
}
What can I do so that the time is always found?
It may fail on a steep curve, you should change the condition to
speed >= c.Evaluate(mid - elipson) && speed <= c.Evaluate(mid + elipson)

In unity how to acces transform rotation in script

I'm making a bouncing and spinning box game. For each spin score will increase.I wrote a code about that but doesn't work. I searched internet but couldn't find answer.
GameObject thePlayer = GameObject.Find("Player");
PlayerEverything player1 = thePlayer.GetComponent<PlayerEverything>();
if (!player1.isGrounded)
{
if(thePlayer.transform.localRotation.z == 0)
{
scorePoint++;
scoreCombo++;
}
if(thePlayer.transform.localRotation.z == 90)
{
scorePoint++;
scoreCombo++;
}
if(thePlayer.transform.localRotation.z == 180)
{
scorePoint++;
scoreCombo++;
}
if(thePlayer.transform.localRotation.z == -180)
{
scorePoint++;
scoreCombo++;
}
if(thePlayer.transform.localRotation.z == -90)
{
scorePoint++;
scoreCombo++;
}
scoreCombo = scorePoint;
score += scorePoint;
}
this is my edited code
if (!player1.isGrounded)
{
currentRotation += Vector3.SignedAngle( transform.parent.right,Vector3.up, transform.right);
if (Mathf.Abs(currentRotation) > 90)
{
scorePoint++;
scoreCombo++;
}
Debug.Log("" + currentRotation);
currentRotation = 0;
}
Multiple issues here!
First of all never compare two float values using ==! Due to the floating point precision this might fail even e.g. for 5f * 0.2f / 10f == 1f .. the result might be 0.9999999 or 1.000000001.
Instead you rather would use a certain range like e.g.
if(Mathf.Abs(a-b) <= someThreshold)
Unity provides Mathf.Approximately
Compares two floating point values and returns true if they are similar.
Floating point imprecision makes comparing floats using the equals operator inaccurate. For example, (1.0 == 10.0 / 10.0) might not return true every time. Approximately() compares two floats and returns true if they are within a small value (Epsilon) of each other.
so using
if(Mathf.Approximately(a-b))
basically equals doing
if(Mathf.Abs(a-b) <= Mathf.Epsilon)
where Mathf.Epsilon is
The smallest value that a float can have different from zero.
Then Transform.rotation and Transform.localRotation is a Quaternion which has four components x, y, z and w. Each of these moves in a range [-1; 1]. Except you know exactly what you are doing (which you don't ;) ) never directly read or write components of a Quaternion!
Your checks will simply never be true!
Instead you should rather work with vectors and check e.g.
// for storing the current rotation
private float currentRotation;
// for storing the last right direction
private Vector3 lastRight;
...
if (!player1.isGrounded)
{
// add the rotation delta since the last frame
currentRotation += Vector3.SignedAngle(lastRight, transform.right, transform.forward);
// if it exceeds +/- 90°
if(Mathf.Abs(currentRotation) > 90)
{
// get points
scorePoint++;
scoreCombo++;
// and reset the rotation counter
currentRotation = 0;
}
}
// Update the last right direction with the current one
lastRight = transform.right;
see Vector3.SignedAngle

Equalize over slope and flat surface velocity in Unity

I've posted a similar question but it is not exactly the same problem so here I go.
I'm not using physics in my project, so every force it's calculated and applied by me. The point is that the character covers the same area in the same time regardless the inclination of the surface is running on.
Being applied the same movement into the CharacterController.Move() function, the velocity of the controller increases exponentially to try to move along the same surface than being in a flat area.
For example. If a prints the movemement calculated that will be applied in the "Move()" function, it is a normalize one, being (0,0,1) in perpendicular and (0.7,0,0.7) in diagonal. However, if I retrieve the velocity via CharacterController.velocity.magnitude I get a different one, being 8 on flat surface and 11.47 in a 45º slope.
I've made a formula to calculate what is the value that should be retrieved with that "velocity.magnitude" function.
groundAngle = Mathf.Abs(Mathf.Round(Vector3.Angle(hit.normal, transform.forward)) - 90);
groundMovementMagnitude = characterController.velocity.magnitude;
slopeMovementIdeal = ((((groundAngle/2) - groundAngle) + 100) * (groundMovementMagnitude / 100));
With this formula, I get in fact a value of "8" in a flat surface and instead of "11.47", the value of the velocity retrieved in a 45º inclination slope is "6.4"
Nevertheless, this value is simply informative, because the velocity of the character controller can not be set. Instead of that, I need a way to modify the movement Vector3 that will be used to trigger the movement, so, instead of moving (0,0,1) on a slope, or (0.7,0,0.7) in diagonal on a slope, apply a reducer to deduct this Vector3 depending on the angle of the slope.
What I've finally done is using the velocity magnitude to calculate the difference between on flat movement and on slope. Then, I just converted that value into a normalized vector and deduct it to the characters movement.
float minMovement = 0.01f;
float difference = 0.0f;
float x = 0.0f;
float z = 0.0f;
if (OnSlope() && characterController.velocity.magnitude - moveSpeed > minMovement) {
difference = characterController.velocity.magnitude - moveSpeed;
//Diagonal
if ((movement.x > minMovement || movement.x < -minMovement) && (movement.z > minMovement || movement.z < -minMovement)) {
x = z = (difference / 2) / characterController.velocity.magnitude;
if (movement.x < 0)
x *= -1;
if (movement.z < 0)
z *= -1;
}
//Perpendicular
else {
if (movement.x > minMovement || movement.x < -minMovement) {
x = difference / moveSpeed;
if (movement.x < 0)
x *= -1;
}
if (movement.z > minMovement || movement.z < -minMovement) {
z = difference / moveSpeed;
if (movement.x < 0)
z *= -1;
}
}
movement.x -= x;
movement.z -= z;
}
It is working fine.

Building pong in Monogame, but balls won't interact with player 1 paddle

I'm pretty new to programming. Basically just finished up a couple tutorials and barebones instructions. I wanted to code pong to get myself started trying to do stuff on my own, but I've hit a bit of a snag. For some reason the ball I generate won't interact with my player 1 paddle at all, but it will interact with my player 2 paddle completely. I can't seem to figure out why since I basically used the exact same code twice just correcting for differences in controls and positions, so any help would be much appreciated. My code for the interaction between the ball and the paddles is as follows:
ballPos += ballSpd * (float)gameTime.ElapsedGameTime.TotalSeconds;
if (ballPos.X == plyr1Pos.X && ballPos.Y + 3 > plyr1Pos.Y - 25 && ballPos.Y - 3 < plyr1Pos.Y +25)
{
ballSpd.X = 150;
ballSpd.Y = 0;
}
else if (ballPos.X == plyr2Pos.X && ballPos.Y + 3 > plyr2Pos.Y - 25 && ballPos.Y - 3 < plyr2Pos.Y + 25)
{
ballSpd.X = -150
ballSpd.Y = 0;
}
I understand there is probably a more efficient way of doing this, but like I said I'm still really new to this and want to learn more, so any constructive criticism is welcome.
Depending on the precision of TotalGameSeconds, the ball might never "hit" the paddle (ballPos.X < plyr1Pos.X). To fix this, try modifying your X detection like so:
if (ballPos.X <= plyr1Pos.X && ballPos.Y + 3 > plyr1Pos.Y - 25 && ballPos.Y - 3 < plyr1Pos.Y +25)
{
ballSpd.X = 150;
ballSpd.Y = 0;
}
else if (ballPos.X >= plyr2Pos.X && ballPos.Y + 3 > plyr2Pos.Y - 25 && ballPos.Y - 3 < plyr2Pos.Y + 25)
{
ballSpd.X = -150
ballSpd.Y = 0;
}
Now it will detect if the ball goes "beyond" the paddle.

Doom-like angle based sprite changing

So, i'm trying to make a first person game that used the same sprite mechanics as games like Doom, Duke Nukem and etc.
So far, i can identify the angle I'm at in relation to static objects, but not to rotating ones. I have some "enemies" that change rotation and start following me, but calculating the tangent angle (Mathf.Atan2) doesn't take the enemy's rotation in consideration.
Here's the code i'm using so far, which works perfectly for objects that dont rotate:
int GetAngleIndex()
{
var dir = cam.transform.position - transform.parent.forward;
var enemyAngle = Mathf.Atan2(dir.z, dir.x) * Mathf.Rad2Deg;
if (enemyAngle < 0.0f)
enemyAngle += 360;
Debug.Log("Angle from the player is: " + enemyAngle);
if (enemyAngle >= 292.5f && enemyAngle < 337.5f)
return 8;
else if (enemyAngle >= 22.5f && enemyAngle < 67.5f)
return 2;
else if (enemyAngle >= 67.5f && enemyAngle < 112.5f)
return 3;
else if (enemyAngle >= 112.5f && enemyAngle < 157.5f)
return 4;
else if (enemyAngle >= 157.5f && enemyAngle < 202.5f)
return 5;
else if (enemyAngle >= 202.5f && enemyAngle < 247.5f)
return 6;
else if (enemyAngle >= 247.5f && enemyAngle < 292.5f)
return 7;
else if (enemyAngle >= 337.5f || enemyAngle < 22.5f)
return 1;
else return 0;
}
I've searched for hours and I can't find a solution to this :(
You say your [only] problem is that it doesn't take their rotation into account - I'm guessing that means that you're not having trouble billboarding your sprites, and your rotation works when they're facing forward. To that end:
The dot product of vectors a and b is equal to cos(theta)*magnitude(a)*magnitude(b). So if a is the vector from the camera to the object:
var a = cam.transform.position - transform.parent.position
and b is the object's forward:
var b = transform.parent.forward
and we know that a and b both have magnitude 1
a.Normalize();
//b is already normalized
then we know that this is equal to cos(theta), where theta is the angle between them.
var theta = Mathf.Acos(Vector3.Dot(a, b)) * Mathf.Rad2Deg;
However. Theta is the shortest necessary angle - so it will be from 0 to 180. Given your switch table up there, we know that when we're hoping to go around the wrong way, we'll be in the wrong position. so to fix that:
if (a.x * a.z < 0)
theta = 360.0f - theta;
then we just plug it in and go. Here's the full file in my project:
using UnityEngine;
public class spriteAngler : MonoBehaviour
{
public Transform toFace;
public SpriteRenderer toManipulate;
public Sprite[] mySprites;
private float theta;
private Vector3 a;
void Update()
{
toManipulate.sprite = mySprites[GetAngleIndex()];
}
int GetAngleIndex()
{
a = toFace.position - transform.position;
a.Normalize();
var b = transform.forward;
theta = Mathf.Acos(Vector3.Dot(a, b)) * Mathf.Rad2Deg;
if (a.x * a.z < 0)
theta = 360.0f - theta;
if (theta >= 292.5f && theta < 337.5f)
return 7;
else if (theta >= 22.5f && theta < 67.5f)
return 1;
else if (theta >= 67.5f && theta < 112.5f)
return 2;
else if (theta >= 112.5f && theta < 157.5f)
return 3;
else if (theta >= 157.5f && theta < 202.5f)
return 4;
else if (theta >= 202.5f && theta < 247.5f)
return 5;
else if (theta >= 247.5f && theta < 292.5f)
return 6;
else if (theta >= 337.5f || theta < 22.5f)
return 0;
else return 0;
}
private Rect guiPos = new Rect(0, 0, 720, 30);
void OnGUI()
{
GUI.Label(guiPos, "Angle from the Player is: " + theta + " and forward=" + transform.forward + " and vectorToTarget=" + a);
}
}
and if that needs a little more context, here's my project: https://github.com/AdamRGrey/22623013
I'd recommend hitting play but watching the scene window instead of the game window.
Maybe I'm not understanding the exact effect you're trying to create here (And if so please provide more information such as screenshots in your post), but you should be able to simply use Transform.LookAt(). These are typically called Billboard Sprites.
Example:
Transform.LookAt(Camera.main.transform.position, Vector3.up)
I guess what you are mentioning is the concept of Billboards.
Here is a sample in unity wiki for creating Billboards that always face Camera, go ahead and give it a try.
http://wiki.unity3d.com/index.php?title=CameraFacingBillboard

Categories