Unity. Disable collision physics but have method for collision called - c#

Basically, i have player object and bunch of other. For this moment they all have RigitBody2D, and colliders, code for creating:
obj = new GameObject();
obj.tag = "web";
obj.name = "web";
float randomX = Random.Range (Utilities.getMainCameraBounds ().min.x, Utilities.getMainCameraBounds ().max.x);
float randomY = Random.Range (Utilities.getMainCameraBounds ().min.y, Utilities.getMainCameraBounds ().max.y);
obj.transform.position = new Vector3(randomX, randomY);
obj.transform.localScale = new Vector3 (3.0f, 3.0f, 3.0f);
collider = obj.AddComponent <BoxCollider2D>();
body = obj.AddComponent<Rigidbody2D> ();
body.constraints = RigidbodyConstraints2D.FreezeAll;
joint = obj.AddComponent<HingeJoint2D> ();
renderer = obj.AddComponent<SpriteRenderer>();
renderer.sortingOrder = 1;
renderer.sprite = Resources.Load("Textures/dot_net", typeof(Sprite)) as Sprite;
Purpose for this is to catch collision like this :
void OnCollisionEnter2D(Collision2D collision) {
if (collision.transform.tag == "web") {
Player.getInstance().joint(collision.rigidbody);
}
}
After player collides with some of those objects, method joint called, it's purpose to connect player and web with HingeJoint2D to simulate pendulum behvior. Code of joint:
public void joint(Rigidbody2D body) {
WebCollection.getInstance ().turnOffColliders (body.gameObject);
HingeJoint2D hingeJoint = webLine.getObject ().AddComponent<HingeJoint2D> ();
hingeJoint.connectedBody = body;
hingeJoint.anchor = new Vector2 (0.00120592f, 0.1921938f);
hingeJoint.connectedAnchor = new Vector2 (0.1939605f, 0.03025406f);
}
Pendulum behavior is working just fine, but in moment of it, or just when player is moving towards web, other web objects are colliding with it, creating real colliding behavior(like crushing into each other)
My goal: i want web objects to react on the collision(calling method above), but on the screen to player just go throw them.
What i've already tried: I wanted to turnOff other objects bodies (WebCollection.getInstance ().turnOffColliders (body.gameObject);) and after pendulum movement is over, to turn them back, but it causes very strange behavior.
What also can be a solution is to manually checking if player is colliding with web object right now, and handle it, but this is to costly i think.

To put my comment into an answer for those finding your question:
If you want to have Colliders that only trigger events and do not physically collide with other Colliders, then what you want are Triggers. To make a Collider a trigger you check the Is Trigger checkbox of the collider component in the inspector (you can also set it via script with the Collider.IsTrigger boolean property).
To do something when the trigger fires, use the OnTrigger events (OnTriggerEnter, OnTriggerExit, OnTriggerStay) (see http://docs.unity3d.com/ScriptReference/Collider.html)
There is also a short introduction to Colliders as Triggers by unity: http://unity3d.com/learn/tutorials/topics/physics/colliders-triggers

Related

Sphere Collider, not pushing things when size is changed

So I'm trying to make a little pushback effect in my tests arena, I've got a sphere collider and here is my script:
// PushBack Class Script
if (Input.GetKeyDown(KeyCode.Q))
{
explosion_ball.transform.position = transform.position;
StartCoroutine(WaitAndPrint());
}
IEnumerator WaitAndPrint()
{
float i = 0;
while (i < 1)
{
i += 0.01f;
explosion_ball.radius = curve.Evaluate(i) * 10;
yield return new WaitForSeconds(0.01f);
}
}
//__________//
Sphere collider is set and stuff, but it doesn't push things back like I thought it would.
Thanks!
Edit:
explosion_ball is a sphere collider, I'm changing it with the point on the animation curve and * it by 10
EDIT:Unity Rigid bodies go to sleep so[Also Change interpolation to continuous if collider changes size too quickly]
A.check if obstacle rigid bodies are Sleeping with onTriggerEnter and Wake Up
void OnTriggerEnter(collider){ if(rb.IsSleeping()){rb.WakeUp();}
or
B.Attach this forceWakeUp Script to all Objects you want to be obstacles.
using UnityEngine;
public class forceWakeUp : MonoBehaviour
{
private Rigidbody rb;
void Start()
{
rb = gameObject.GetComponent<Rigidbody>();
}
void Update()
{
if(rb.IsSleeping()){rb.WakeUp();}
}
}
Forcibly Keeping Many objects awake Will impact performance.So ,your decision.
You need to scale up the collider on the object by radius and all the objects it is supposed to wobble need to have Rigid body component attached to them
2.if you ARE doing above things and its not adding any force you could just add a force on all the overlapping objects using the "OnCollisionEnterTrigger" and AddForceMethod radially away from the Sphere
Obstacle.position - Sphere.position is the vector Radially away from Sphere I think.
You don't need coroutines for this I think.
While searching through the Unity scripting API, found a method in there called
Rigidbody.AddExplosionForce(explosionForce, explosionPosition, explosionRadius, upwardsModifier, mode)
In order to get the rigid bodies that are to be affected by the explosion I would need to get them using Physics.OverlapSphere(position, radius) this gets all of the objects within the radius variable, then get the component.
Combining these two would also look like:
Collider[] colliders = Physics.OverlapSphere(transform.position, radius);
foreach (Collider hit in colliders)
{
if (hit.transform.tag == "Interactable")
{
if (hit.gameObject != hitgameObject)
{
Rigidbody rb = hit.GetComponent<Rigidbody>();
if (rb != null)
rb.AddExplosionForce(power, transform.position, radius, upForce, ForceMode.Impulse);
}
}
}
If there is any explaining that you would like me to do about my variables mentioned I will reply :)
Thanks for the help guys.
I don't know your curve and what values that evaluation produces, but you can check your code visually with Window/Analysis/Physics Debugger, or write a gizmo:
private void OnDrawGizmos()
{
Gizmos.color = new(1.0f, 0.0f, 0.0f, 0.5f);
Gizmos.DrawSphere(explosion_ball.transform.position, explosion_ball.radius);
}
Or simply just use https://docs.unity3d.com/ScriptReference/Rigidbody.AddExplosionForce.html

Ranged attack in Unity3D

I have two objects: a player and an enemy. The main problem is the enemy, which should start shooting at the player when it approaches him at a certain distance (in short, just start the animation).
At first I tried to implement this through Vector 3, as with other ordinary opponents. But he is as stupid and crutch as possible, however, you yourself can see everything in the pinned.
I started to implement it more allegedly correctly, through the trigger and the collider (box collider) of the enemy itself. And everything works right as it should, but there is a nuance. The enemy also has boxing implemented through the box collider, the player, hitting which, causes damage to him. There is only one box collider for these two tasks, and since I had to increase this collider so that the enemy could stop in front of the player at a certain distance. Because of this, the player can hit at a great distance (at which the enemy can shoot) from the enemy and the enemy takes damage anyway.
I tried to make a separate object the size of the enemy himself and use it as a box to receive damage. Then he already transmits data about receiving damage to the enemy object. But this does not work, all links between scripts and objects are made, but he does not want to transfer data. That is, simply making two colliders for different tasks does not work.
In general, here my powers are all. I searched the entire Internet, but I did not find how to implement this mechanic in a different way so that it does not conflict with others. Therefore, I ask the help of the local experts, where I screwed up.
private Animator ch_animator;
// Start is called before the first frame update
void Start()
{
myAgent = GetComponent<NavMeshAgent>();
myAnim = GetComponent<Animator>();
EnemyH = GetComponent<GDHealth>();
}
// Update is called once per frame
void Update()
{
dist = Vector3.Distance(/*checker.*/transform.position, target.transform.position);
if (dist > range)
{
myAgent.enabled = false;
myAnim.SetBool("Idle", true);
myAnim.SetBool("Move", false);
myAnim.SetBool("Attack", false);
}
if (dist <= range & dist > atRange)
{
myAgent.enabled = true;
myAgent.SetDestination(target.position);
myAnim.SetBool("Idle", false);
myAnim.SetBool("Move", true);
myAnim.SetBool("Attack", false);
}
if (dist <= atRange)
{
StartCoroutine(Attack());
}
if (PlayerH._health <= 0)
{
atRange = 0;
}
if (EnemyH._health < 0)
{
myAgent.enabled = false;
}
}
public IEnumerator Attack()
{
yield return new WaitForSeconds(0.5f);
myAgent.enabled = false;
myAnim.SetBool("Idle", false);
myAnim.SetBool("Move", false);
myAnim.SetBool("Attack", true);
}
void OnTriggerStay(Collider col)
{
if (col.tag == "Player")
{
//gameObject.GetComponent<Animator>().SetBool("Attack", true);
StartCoroutine(Attack());
transform.LookAt(col.transform.position);
transform.eulerAngles = new Vector3(0, transform.eulerAngles.y, 0);
}
}
void OnTriggerExit(Collider col)
{
if (col.tag == "Player")
{
myAgent.enabled = true;
myAgent.SetDestination(target.position);
myAnim.SetBool("Idle", false);
myAnim.SetBool("Move", true);
myAnim.SetBool("Attack", false);
//gameObject.GetComponent<Animator>().SetBool("Attack", false);
}
}
But this does not work, all links between scripts and objects are made, but he does not want to transfer data.
From this description it could be anything: wrong layers, no rigidbody on either of objects, misstyped tags in OnTriggerStay method.
In my project, I successfully created 2 colliders for 2 separate tasks, so this is how I would see it in your problem:
Use two colliders
Attach one collider with one script to the enemy object - this collider should have a size of the enemy. The OnTriggerStay method here should deal damage to the enemy, check for death, etc.
Create child object to the enemy. Attach new collider to it with the size of enemy's attack range. Attach a script with OnTriggerStay method that will stop enemy and begin ranged attack (or whatever you want to do).
If this doesn't work: check collision matrix or try adding a kinematic rigidbody to either of objects.
Measure distance between player and the enemy in update (which you are already doing) and apply necessary code based on distance (stop or attack) thus replacing one of the colliders.
Hope that helps!

Problem with respawned camera in Unity3D in c#

I have a problem with camera in Unity. I want to follow to the ball. I want to make auto respawn for the ball and camera. But the code works but camera doesn't follow to the ball.
This is code for auto respawn:
public class start : MonoBehaviour
{
public GameObject Camera;
public GameObject Ball;
void Start()
{
GameObject ball = GameObject.Instantiate(Ball);
Ball.name = "Ball";
ball.transform.position = transform.position + Vector3.up * 0.5f;
Instantiate(Camera);
Camera.name = "Camera";
Camera.transform.position = transform.position + new Vector3(0.25f, 1.75f, 6.5f);
}
The code for camera controller:
public Transform Ball;
void Update()
{
Rigidbody rigidbody = Ball.GetComponent<Rigidbody>();
float ballVelocity = rigidbody.velocity.sqrMagnitude;
UnityEngine.Vector3 vector = new UnityEngine.Vector3(0, 4, 7);
vector *= (1+ ballVelocity / 30);
UnityEngine.Vector3 nowaPozycjaKamery = Ball.position + vector;
transform.position = UnityEngine.Vector3.Lerp(transform.position, nowaPozycjaKamery, Time.deltaTime*5);
transform.LookAt(Ball);
}
Camera worked well before auto respawn. Code work that on start camera follow to good position, but is freezed when the ball change the position.
I wish you provided more information on this question. Like is there any NullReferenceException or other exceptions. But I'll try to answer your issue anyways. I think the problem is that the camera script doesn't have the reference to the new ball that's being spawned (this is in the case of destroying the old ball and Instantiating a new one with Destroy(gameObject)).
What I suggest is to add a Method in the camera script that will be in charge of injecting the new ball into the script. And then you call this method with the new spawned ball in the script responsible for spawning. Or a more straightforward but not efficient way is to check if the ball property is null and then you make a call to GameObject.Find("Name of The ball object") and you give the name of the ball object.

How to move a RigidBody2D to a position while still checking for collision

I am currently creating a game in Unity, in which you move a ball around using OnMouseDrag(), a CircleCollider2D and a RigidBody2D. This is how I set the position of the ball:
private void OnMouseDrag()
{
Vector2 mouseInWorld = Camera.main.ScreenToWorldPoint(Input.mousePosition);
playerRb.position = new Vector3(mouseInWorld.x, mouseInWorld.y, 0);
}
I still want the ball to slide on collision while the mouse moves around. Is there a way to do this?
I have tried RigidBody2D.MovePosition(), but the ball jumped around from one point to another, and Raycasts but couldn't get that to work either.
EDIT:
This is what I've got now:
playerRb.velocity = new Vector3(mouseInWorld.x - playerRb.position.x, mouseInWorld.y - playerRb.position.y, 0);
Now the problem is, that the ball lags behind the mousePosition.
When you use RigidBody.MovePosition, you don't call the physics engine and so it ignores collisions. If you want collisions to happen you need to use RigidBody.Velocity instead.
Doing this change will require you to make some change to your code though because what you give to RigidBody.Velocity is a velocity and not a position so you will need to calculate the velocity required in x,y (and z if you are in 3d) to reach your destination.
I invite you to read the Unity page about velocity for more info
https://docs.unity3d.com/ScriptReference/Rigidbody-velocity.html
Note: This will make the player/ball stick to collisions.
Modifying the velocity could cause the ball to bounce around unexpectedly when the ball collides with the wall. I would use a CircleCast for this, check if it hit anything, then use MovePosition accordingly:
float cursorDepth;
Rigidbody2D playerRb;
CircleCollider cc;
void Awake()
{
playerRb = GetComponent<Rigidbody2D>();
cc = GetComponent<CircleCollider>();
}
private void OnMouseDrag()
{
Vector2 mouseInWorld = Camera.main.ScreenToWorldPoint(Input.mousePosition);
Vector2 posToMouse = mouseInWorld - playerRb.position;
RaycastHit2D hit = Physics2D.CircleCast(playerRb.position,
cc.radius * transform.lossyScale.x, posToMouse, posToMouse.magnitude);
if (hit.collider != null)
{
mouseInWorld = hit.centroid;
}
playerRb.MovePosition(mouseInWorld);
}
But notice that if the ball can't move all the way to the mouse, it might cause the drag to end. So, plan accordingly.

A better way to detect the cursor?

Goal: Detect when the cursor has entered a defined radius of the player.
Hello, I am in the process of trying to replicate the combat system from a game called CrossCode. The idea is that when the cursor is within a certain radius of the player, I will be able to switch to melee combat, and back to ranged once the cursor leaves this radius.
I have implemented one way I thought it could be done, however it feels slow or unreliable and I just wanted to know if there are any other methods I could possibly look into to achieve a smoother result.
Here is what I've done
attached to the player
void Update()
{
attackStyleSwitchRadius = colRef.radius;
playerCenter = colRef.transform.position;
if(Physics2D.OverlapCircle(playerCenter, attackStyleSwitchRadius, cursor))
{
meleeMode = true;
rangeMode = false;
}
else
{
meleeMode = false;
rangeMode = true;
}
}
And on a small 2D object I have this script so that it follows the cursor position.
void Update()
{
pos = Input.mousePosition;
gameObject.transform.position = Camera.main.ScreenToWorldPoint(pos);
}
when the small object enters the overlap circle it changes the bools around.
You can remove the collision detection overhead by doing something like this instead;
void Update ()
{
attackStyleSwitchRadius = colRef.radius;
playerCenter = colRef.transform.position;
var mouse = Input.mousePosition;
mouse.z = Vector3.Distance(Camera.main.transform.position, playerCenter);
var range = Vector2.Distance(Camera.main.ScreenToWorldPoint(mouse), playerCenter);
var inside = range < attackStyleSwitchRadius;
meleeMode = inside;
rangeMode = !inside;
}
Cursor
Make an object, name it cursor.
Add a small collider to the cursor object.
Add a script to the cursor object so its always at the mouses location.
Melee range zone
Add a GameObject as child of the player, name it MeleeRangeZone.
Add a collider to it, set it to be a Trigger. The size of this collider will be the players melee range,
Add a rigidbody to it so that collisions can be detected, but set the rigidbody to not rotate or change its position.
Add a script to the object and use the OnTriggerEnter and OnTriggerExit methods to detect whether or not the cursor has entered your melee zone.
You can now use the OnTriggerEnter and OnTriggerExit methods to switch between the players attack modes, meaning that when the cursor enters it changes to melee and when it exit it changes to ranged.
You can fire the ray to detect the location the cursor should have like this:
public LayerMask RaycastCollidableLayers;
public RaycastHit Hit;
public float CheckDistance = 200f;
public Transform Cursor;
void PerformRaycast(){
//Set up ray
ray = Camera.main.ScreenPointToRay(Input.mousePosition);
//Fire ray
Physics.Raycast(ray, out Hit, CheckDistance + 0.1f, RaycastCollidableLayers);
if (Hit.collider == null)
{
//Debug.Log("Raycast hit nothing");
return;
}
else //Ray hit something
{
//Move cursor to Hit.point;
Cursor.position = Hit.point;
}
}

Categories

Resources