OnCollisionEnter2D executing before Start on prefab - unity3d

I know my code is ugly, I welcome any improvement suggestions. My goal seems simple, spawn 75 "bubbles" (prefabs) on the screen in random positions at random rotations, and have them move with a random velocity between 0.3 and 0.7, or -0.3 and -0.7. When they collide with a wall, calculate a new random velocity of the same range with respect to which wall it hit.
Currently how I am doing this:
GameManager object has a script that instantiates the bubbles
Bubble prefab has a script that sets the velocity.
How I IMAGINED it going: When the prefab script executes, the start routine would determine an initial random velocity. Then, the OnCollisionEnter2D would be called when the bubble hits a wall and it would recalculate a velocity going in whatever opposite direction the wall is (if it hits the bottom wall, set velocity to a random positive Y value, if it hits the right wall, set velocity to a random negative X value, etc).
How it is ACTUALLY going: Debug logs show the OnCollisionEnter2D executing before Start. This is causing bubbles to get "stuck" in the corner of the screen, and other unexpected behavior. See images of Bubble #70 it is stuck in the bottom left corner with the Rigidbody showing a velocity of 0,0. Debug Log shows the velocity was first calculated in the collision routine, then last calculated in the Start method as -0.7, -0.7. Obviously being stuck in the bottom left, it can't go any lower so it froze it to 0,0. This wouldn't have happened if Start had run first and THEN the OnCollisionEnter2D because the collision routine would have detected the bottom walls and set the velocity to a positive value. Now that it's stuck, it can't collide again.
I'm not sure what to do, I can put something in Start to check the position first, but I feel like I have a bigger problem with my code running the OnCollisionEnter2D before Start, and that would only be a bandaid.
GameManager script to instantiate bubble prefab:
void Start()
{
buffer = 10f;
objectCount = 75;
for (int loop = 0; loop < objectCount; loop ++){
worldMin = Camera.main.ScreenToWorldPoint(new Vector2(buffer, buffer));
worldMax = Camera.main.ScreenToWorldPoint(new Vector2(Screen.width - buffer, Screen.height - buffer));
spawnPosition = new Vector2(
Random.Range(worldMin.x, worldMax.x),
Random.Range(worldMin.y, worldMax.y));
GameObject newGO = (GameObject)Instantiate (objectToSpawn, spawnPosition, Quaternion.Euler(new Vector3(0, 0, Random.Range(-40.0f, 40.0f))));
newGO.name = newGO.name + loop;
}
Bubble prefab script to set velocity (my reason for the if statements are because if the velocity random value is very close to 0 the object moves TOO slow):
void Start()
{
Physics2D.IgnoreLayerCollision(gameObject.layer, gameObject.layer);
min = -0.8f;
max = 0.8f;
compareMax = max - 0.5f;
compareMin = min + 0.5f;
moveX = Random.Range(min, max);
moveY = Random.Range(min, max);
if (moveX < 0.0f && moveX > compareMin){
moveX = min;
}
if (moveX > 0.0f && moveX < compareMax){
moveX = max;
}
if (moveY < 0.0f && moveY > compareMin){
moveY = min;
}
if (moveY > 0.0f && moveY < compareMax){
moveY = max;
}
Debug.Log("Start name: " + gameObject.name + ", moveX: " + moveX + ", moveY: " + moveY);
rb = GetComponent<Rigidbody2D>();
rb.velocity = new Vector2(moveX, moveY);
}
void OnCollisionEnter2D(Collision2D col){
rb = GetComponent<Rigidbody2D>();
if (col.gameObject.tag == "Border"){
min = -0.8f;
max = 0.8f;
compareMax = max - 0.5f;
compareMin = min + 0.5f;
moveY = Random.Range(min, max);
moveX = Random.Range(min, max);
if (col.gameObject.name == "BottomWall"){
moveY = Random.Range(compareMax, max);
}
if (col.gameObject.name == "TopWall"){
moveY = Random.Range(min, compareMin);
}
if (col.gameObject.name == "LeftWall"){
moveX = Random.Range(compareMax, max);
}
if (col.gameObject.name == "RightWall"){
moveX = Random.Range(min, compareMin);
}
Debug.Log("OnCollision name: " + gameObject.name + ", moveX: " + moveX + ", moveY: " + moveY);
rb.velocity = new Vector2(moveX, moveY);
}

Related

Unity quaternion lookrotation behaving oddly

I am using unity to create an airline game. I made the takeoff code as the following:
`
Rigidbody m_Rigidbody;
float thrust_x = 2;
float thrust_y = 0;
float timeRemaining = 1.5f;
// Start is called before the first frame update
void Start()
{
m_Rigidbody = GetComponent<Rigidbody>();
}
void TakeOff()
{
m_Rigidbody.AddForce(thrust_x, thrust_y, 0, ForceMode.Impulse);
transform.rotation = Quaternion.LookRotation(new Vector3(thrust_x, thrust_y, 0));
if (timeRemaining > 0)
{
timeRemaining -= Time.deltaTime;
}
else if (timeRemaining < 0 && thrust_y < 1.5)
{
thrust_y += 0.1f;
}
}
`
The timeRemaining waits for 1.5 seconds and then add to the thrust_y. The lookrotation makes the plane point in the direction of the impulse vector. This works impeccably with the plane as a cube. When I replaced it for a model, the plane strangely adds 90deg to the y rotation for no apparent reason.
Before running:
While running:
How can I make the plane once again point to 0,0. Turning the plane 90 and -90 at the start does not work, which gives reason that the value is being set, not changed.
Any help will be greatly appreciated. Thanks!

Finding angle of swipe in Unity3D

I need to find the angle between the swipe in Unity3D. In the update function I'm using the following code:
private void Update()
{
if (Input.GetMouseButtonDown(0))
{
fingerup=Input.mousePosition;
fingerdown=Input.mousePosition;
}
if (Input.GetMouseButtonUp(0))
{
fingerdown=Input.mousePosition;
CheckSwipe();
}
}
and in the CheckSwipe function I'm doing this:
CheckSwipe()
{
var directionvector = fingerup-fingerdown;
if(directionvector.magnitude>SwipeThreshold)
{
/* I need to find the angle between the fingerup and the fingerdown vectors and based on that angle I need to check if the user swiped left, right, up or down. */
/*if (angle>= 45 && angle< 135) onSwipeUp.Invoke();
else if (angle>= 135 && angle < 225) onSwipeRight.Invoke();
else if (angle>= 225 && angle< 315) onSwipeDown.Invoke();
else if (angle>= 315 && angle< 360 || angle >= 0 && angle< 45) onSwipeLeft.Invoke();*/
}
}
How can I get the angle of swipe? I tried various solutions, but it was not working. Any help from any one is appreciated. Thanks in advance.
First thing, during button mouse up event, you should assign value to fingerup variable, not down.
If this doesn't fix your issue, I came up with idea to use trigonometry to calculate the angle:
if (Input.GetMouseButtonDown(0))
{
fingerup = Input.mousePosition;
fingerdown = Input.mousePosition;
}
if (Input.GetMouseButtonUp(0))
{
fingerup = Input.mousePosition;
double angle = Mathf.Atan((fingerup.x - fingerdown.x) / (fingerup.y - fingerdown.y)) * Mathf.Rad2Deg;
if (fingerup.y < fingerdown.y) angle += 180;
if (fingerup.y > fingerdown.y && fingerup.x < fingerdown.x) angle += 360;
Debug.Log("fingerDownPos : " + fingerdown + " fingerUpPos: " + fingerup + " angle: " + angle);
}
Probably it can be written better, but I had no other idea but to use ifs and add angles depending on start and end. Anyway, it's working, and should be a good start.
Edit: your question is about an angle, but I just realized, that since you need only the general direction of a swipe (left, right, up, down) you don't need to calculate angle. Just substract fingerup.x - fingerdown.x and fingerup.y - fingerdown.y Compare which absolute value of this is bigger. If abs from vertical is bigger than horizontal, then it will be either up or down, up if the substraction is greater than 0. You get the idea how to do the rest.

Prevent Diagonal movements

I'm developing a 3D tactical RPG game and I would like to know how I could prevent diagonal movements when the NPC moves? I want him to detect where the human player is in the grid and then go after him.
I can take their grid position but it didn't work. The NPC makes diagonal movements and starts to "sink" in the grid.I've tried something like this, target position is a vector 3 and I call the function in Update(), this script is attached to the NPC:
public void MoveNPC()
{
HumanPlayer = GameObject.FindGameObjectWithTag("Player");
ScriptPlayer PlayerPosition = HumanPlayer.GetComponent<ScriptPlayer>();
NPC NPCPosition = this.GetComponent<NPC>();
if ((PlayerPosition.GridPosition.x - NPCPosition.GridPositonNPC.x) + PlayerPosition.GridPosition.y - NPCPosition.GridPositonNPC.y) >= movMax &&
(PlayerPosition.GridPosition.x -NPCPosition.GridPositonNPC.x) + (PlayerPosition.GridPosition.y - NPCPosition.GridPositonNPC.y) <= movMax)
{
if((PlayerPosition.GridPosition.x - NPCPosition.GridPositonNPC.x) - (PlayerPosition.GridPosition.y - NPCPosition.GridPositonNPC.y) >= movMax &&
(PlayerPosition.GridPosition.x - NPCPosition.GridPositonNPC.x) - (PlayerPosition.GridPosition.y - NPCPosition.GridPositonNPC.y) <= movMax){
targetPosition = HumanPlayer.transform.position;
targetPosition.y = 1.4f;
}
}
if(this.transform.position != targetPosition)
{
this.transform.position = Vector3.MoveTowards(transform.position, targetPosition, velocity * Time.deltaTime);
}
}
How could I solve it?

find the heading angle between 2 objects taking into account the forward angle of the initial object

Ok, so, i've been stuck on this for ages. Im working on an AI that will navigate a tank to a waypoint, defined as a Vector3. the position of the tank is also defines as a Vector3, both these have their Y position set to 0, as to ignore terrain elevation, the current rotation of the tank is also a Vector3, though only the Y rotation is needed, as i'm effectively projecting the 3d position onto a 2d navigational grid.
The AI passes anywhere between -1 and 1 into the control for the tank, which then handles the physics operations. so, i need to somehow calculate the angle, positive or negative in relation to the current heading angle of the tank to the position of the waypoint, then send the rotation value to the controls. At the moment I simply cant get it working, I feel like iv'e pretty much tried everything.
This is my code currently, it doesn't work, at all, and is about the 20th revision:
void driveToTarget()
{
Vector3 target0 = driveTarget;
target0.y = 0;
GameObject current0Obj = new GameObject();
Vector3 current0 = this.transform.position;
current0.y = 0;
print(current0);
print(target0);
Vector3 current0Angle = this.transform.eulerAngles;
print(current0Angle.y);
current0Angle.x = 0;
current0Angle.z = 0;
Vector3 heading = target0 - current0;
Quaternion headingAngle = Quaternion.LookRotation(heading);
print("headingAngle" + headingAngle);
print("heading direction, allegidly: " + Quaternion.Euler(heading).ToEulerAngles());
Quaternion distanceToGo = Quaternion.Lerp(Quaternion.Euler(current0Angle), headingAngle, 0.01f);
float angle = Vector3.SignedAngle(current0, target0, Vector3.up);
float difference = Mathf.Abs(angle - current0Angle.y);
print("heading angle " + angle);
if (current0 != driveTarget)
{
steeringVal = Mathf.Abs(1.5f-(1f/Mathf.Abs(distanceToGo.y))) * -Mathf.Sign(distanceToGo.y); ;
throttleVal = 0f;
} else
{
throttleVal = 0;
}
}
--EDIT--
So, I've partially solved it, and now encountered another problem, I've managded to get the tank to detect the angle between itself and the waypoint, BUT, rather than orienting forward towards the waypoint, the right side of the tank orients towards it, so it orbits the waypoint. I actually know why this is, becasue the forward vector of the tank is technically the right vector because of unity's stupid axis ruining my blender import, anyway, heres the updated code:
void driveToTarget()
{
Vector3 target0 = driveTarget;
target0.y = 0;
Vector3 current0 = this.transform.position;
current0.y = 0;
print("Current: " + current0);
print("Target: " + target0);
Vector3 current0Angle = this.transform.rotation.eulerAngles;
print("Curret rotation:" + current0Angle.y);
current0Angle.x = 0;
current0Angle.z = 0;
Vector3 heading = target0 - current0;
Quaternion headingAngle = Quaternion.LookRotation(heading);
print("heading angle: " + headingAngle.ToEuler());
float distanceToGo = (current0Angle.y) - headingAngle.eulerAngles.y;
print("DistanceToGo: " + distanceToGo);
if (current0 != driveTarget)
{
steeringVal = 1 * -Mathf.Sign(distanceToGo);
throttleVal = 0f;
} else
{
throttleVal = 0;
}
Debug.DrawRay(current0, heading, Color.red, 1);
Debug.DrawRay(current0, this.transform.up, Color.red, 1);
}
I'm not sure exactly how your code is setup or how the steering works. You may want to look into using the Unity NavMeshAgent to simplify this.
Regardless here is some code I wrote up that takes a destination and rotates an object towards it. All you'd have to do from there is move the object forwards.
Vector3 nextDestination = //destination;
Vector3 direction = nextDestination - transform.position;
direction = new Vector3(direction.x, 0, direction.z);
var newRotation = Quaternion.LookRotation(direction);
var finalRotation = Quaternion.Slerp(transform.rotation, newRotation, Time.deltaTime); //smoothes out rotation
transform.rotation = finalRotation;
Sorry if this isn't what you needed. Have you been able to figure out which part of the code is behaving unexpectedly from your print statements?

Unity - following finger only works for one direction

I have made some code that should make my player move into the direction of the finger:
if (Input.touchCount == 1 && Input.GetTouch(0).phase == TouchPhase.Moved)
{
Vector2 pos = Camera.main.ScreenToWorldPoint(new Vector3(Input.GetTouch(0).position.x, Input.GetTouch(0).position.y, 0));
if (pos.x < rb.position.x)
{
movehorizontal = -1;
}
if(pos.x > rb.position.x)
{
movehorizontal = 1;
}
if (pos.y < rb.position.z)
{
movevertical = -1;
}
if(pos.y > rb.position.z)
{
movevertical = 1;
}
}
Vector3 movement = new Vector3(movehorizontal, 0.00f, movevertical)*speed;
It's a 3D game, with a top-view, so my player starts at 0,0,0 and only moves along the x and z axis. My camera is positionated on 0,10,3. The following works on the x axis, so when my finger touches on the right side it goes to the right, if on the left to the left, but no matter where I touch it, it will only move to the front and not to the bottom of my screen.
I tried debugging, but the instructions werent working at the time.
screentoWorldPoint should be stored as a vector3. also since the camera is 10 units away from your plane the last parameter should be 10.
edit, that will only work for a cam pointing straight down. this code should work regardless of the camera angle.
Vector3 pos = Camera.main.ScreenToWorldPoint(new Vector3(Input.GetTouch(0).position.x, Input.GetTouch(0).position.y, 1f));
Vector3 pointDelta = pos - Camera.main.transform.position;
float multiplier = -Camera.main.transform.position.y / pointDelta.y;
pos = Camera.main.transform.position + pointDelta * multiplier;
finally these lines should compare the z values to each other
if (pos.z < rb.position.z)
if(pos.z > rb.position.z)
make those changes and let us know if any other problems still exist