I'm kind of new to Unity. As by the title, I am having trouble getting the collisions right in my game. I am using the custom physics script from unity: https://unity3d.com/learn/tutorials/topics/2d-game-creation/scripting-gravity?playlist=17093. In my game, I am experiencing difficulties in disable collisions.
For example, when I use
Physics2D.IgnoreLayerCollision (8, 9);
It doesn't change anything and the two characters still collide. Also, for some reasons, the triggers behave strange and are still affected by collisions. Going near a character with triggers will make him float up. I've been stuck on this for a long time and I'd really appreciate the help.
It is worth noting that I am using custom physics and using a box collider 2d to detect attack ranges etc. Here is some code of that:
Collider2D attackbox = Physics2D.OverlapBox (attackPos.position, new Vector2 (attackRangeX, attackRangeY), 0, whatIsPlayer);
if (attackbox != null && !isDead && characterController.isDead == false) {
targetVelocity = Vector2.zero;
animator.SetTrigger ("isPunching");
timeBtwAtk = startTimeBtwAtk;
}
and I have individual punch triggers in the animation to detect when the character is actually being hit:
public void SetColliderIndex(int spriteNum)
{
colliders[currentColliderIndex].enabled = false;
currentColliderIndex = spriteNum;
colliders[currentColliderIndex].enabled = true;
}
public void ClearColliderIndex()
{
colliders[currentColliderIndex].enabled = false;
}
void OnTriggerEnter2D(Collider2D col)
{
if (col.tag == "Player")
{
col.GetComponent<CharacterController2D> ().TakeDamage (enemyDamage);
col.GetComponent<CharacterController2D> ().canWait = false;
col.GetComponent<CharacterController2D> ().canControl = false;
}
}
When I use Physics2D.IgnoreLayerCollision (8, 9); I want both specified layers not to interact whatsoever. I don't want any weird shifts, or floating when they pass through each other. Also when I move my player agains the enemy, I don't want him to be able to push him back. I want it to be as if he is running into a wall.
I have answered something similar to this here. Also, if you don't want the player suddenly floating upon collision, you can just set the rigidbody/rigidbody2d type to kinematic, but this mean the object will not be affected by physics. you will have to do that in code.
i think your problem is that at some point the two colliders interact and/or one of the two gets activated back, but from the code that you posted i can't clearly identify the problem if there's any
Related
In the title it is not easy to summarize this behavior in the best way.
I have an Editor and a Monobehaviour executed in [ExecuteInEditMode].
I need to move objects by selecting them with the mouse. I thought I didn't encounter any difficulties as I perform this operation with both a character and sprite. But I was wrong. When I select a 3D object (not the character) I can pick and drag it but if I hold the mouse button down without moving it it tends to slip away as if it were falling but on the Y and Z axes. If I deselect its collider the issue does not happen but obviously the collider is necessary for my operations.
I have removed all the unnecessary lines from the code, hoping to solve the problem, but it remains.
void OnGUI() {
if (moveChar) {
StartCoroutine("WaitChar");
} else if (moveSpot) {
StartCoroutine("WaitSpot");
}
Event e = Event.current;
Vector3 mousePosition = e.mousePosition;
mousePosition.y = Screen.height - mousePosition.y;
Ray ray = cam.ScreenPointToRay(mousePosition);
RaycastHit hitInfo;
if (Physics.Raycast(ray, out hitInfo)) {
mousePositionWorld = hitInfo.point;
if (e.rawType == EventType.MouseDown) {
switch (e.button) {
case 0:
if (isSelectedSphere) {
moveSpot = true;
}
break;
case 1:
if (isSelectedChar) {
moveChar = true;
}
break;
}
} else if (e.rawType == EventType.MouseUp) {
switch (e.button) {
case 0:
if (moveSpot) {
moveSpot = false;
}
break;
case 1:
if (moveChar) {
moveChar = false;
}
break;
}
}
}
}
public IEnumerator WaitChar() {
character.transform.position = mousePositionWorld;
yield return new WaitForSeconds(0.1f);
}
public IEnumerator WaitSpot() {
MoveSpot.transform.position = mousePositionWorld;
MoveSpot.transform.localScale = Vector3.one * ((new Plane(cam.transform.forward,
cam.transform.position).GetDistanceToPoint(
MoveSpot.transform.position)) / radiusPoint);
yield return new WaitForSeconds(0.1f);
}
I have simplified everything with two public variables to choose the object to move.
What I do is very simple, I don't understand why I get this behavior. With the character I have no problem (not even with the sprites, which for the moment I removed from the code). When I click with the right mouse button I run the coroutine that moves the character to the cursor coordinates and continues like this until I release the button. No problem, the character moves along with the cursor without problem.
However, if I execute the same operation (left button) with a 3D object like Sphere, Cylinder, Cube it moves following the mouse but if I don't move the cursor and don't release the button the object slides away with the Y and the Z axes that increase in negative. As I said, if I deactivate the collider the object remains at the cursor position.
The problem is that the spheres cannot be selected through public variables but directly from the script so I need the collider.
I probably don't know Unity well enough to know the reason for this behavior.
Can you clarify this for me?
OnGui is bad (let me explain)
First, OnGui is part of Unity's IMGui system (stands for IMmediate Gui), and has been heavily depreciated in favor of uGui, which is a lot easier to work with for a number of reason. And one of those reasons is....
OnGui is called more than once per frame
Because OnGui is called multiple times per frame, that means that Event.current hasn't changed, meaning moveChar hasn't changed, meaning your coroutine gets started more than once.
And there's no real way around this. Its going to be almost impossible to do what you want using OnGui. It was never meant to handle this sort of logic.
Everything you have should be done inside Update() instead and replacing things like e.rawType == EventType.MouseDown with their Input relatives, eg. Input.GetMouseDown(0).
Hi guys I am having a problem in unity with OnTriggerEnter2D.
The player has 2 bullets, any time the player bullet hits the enemy it should increment the score by 100, but for some reason it is incrementing the score by 200 if two player bullets hit the enemy. Both bullets fire at the same time and are at either side of the players gun. So it should only ever increment the score by 100 and not 200.
Here is my code:
void OnTriggerEnter2D(Collider2D col)
{
if(col.tag == "PlayerBullet")
{
this.score += 100;
}
}
Other information:
My player bullet and enemy both have Box Collider and RigidBody2D attached to them. They both have the isTrigger option checked.
If you want it to work just once you can ignore collision after first increment of the score(aka after first collision) like this:
void OnTriggerEnter2D(Collider2D col)
{
if(col.tag == "PlayerBullet")
{
this.score += 100;
Physics2D.IgnoreCollision(col, GetComponent<Collider2D>())
}
}
Note that After this point all collisions between PlayerBullet and Enemy will be ignored.
If I understand it correctly, the player has two guns, and I assume they are fired at the same time.
In this case, we can make it more beautiful with condition-condition statements.
I'm leaving the code block below.
if (Input.GetMouseButtonDown(0) && Input.GetMouseButtonDown(1))
{
Debug.Log("Fire Metod(Score)");
}
else
{
if (Input.GetMouseButtonDown(0))
{
Debug.Log("Fire Metod(Score)");
}
if (Input.GetMouseButtonDown(1))
{
Debug.Log("Fire Metod(Score)");
}
}
Often times you can set a flag to tell the game to only increment once. A flag is just a bool value, and can be private within the scope of the class. Sort of like:
if(canHit)
{
canHit = false;
//add score
canHit = true;
}
That basic formula should get you the results you want.
I'm upgrading my comment to an answer because after reading your question and the comment responses I believe you need a non-code answer.
You want two objects to work together and count as one 'hit' when they collide with another object. That sounds to me like a situation where you should be placing those objects inside another one, let's call it a 'shot' object, and you should be doing collisions and whatnot based on that. That way, if both bullets hit only one 'shot' hit, so it will count with the code as-is and it sounds like it will be implemented more as you've expected it to be.
Allright, I have gameObjects with tag "Enemy" and I collect them like this:
enemies = GameObject.FindGameObjectsWithTag("Enemy");
After that, I am trying to move the first "Enemy":
int newPositionX;
int newPositionY;
bool targetReached = false;
int moveSpeed = 1;
void Update()
{
if (!targetReached)
{
newPositionX = Mathf.FloorToInt(enemies[0].transform.position.x)-1;
newPositionY = Mathf.FloorToInt(enemies[0].transform.position.y);
enemies[0].transform.position = Vector3.MoveTowards(enemies[0].transform.position,
new Vector3(newPositionX, newPositionY), moveSpeed * Time.deltaTime);
targetReached = true;
}
if (Vector3.Distance(enemies[0].transform.position, new Vector3(
newPositionX, newPositionY)) < 0.1f)
{
targetReached = false;
}
}
But an "Enemy" is not making any moves. I can edit that if I remove the:
targetReached = true;
the "Enemy" is moving but there's no way I can stop it. That makes me think that to make sth moving MoveTowards function should be called permanently in the Update?
MoveTowards returns a vector that moves the gameobject one step in the direction of the target.
Also, I am not sure about this, but looking at your code, the if statement that sets the targetReached to false only runs if the targetReached is already false, so you may want to put that out of the if (!targetReached) statement
You probably had the idea that Unity is a complete game engine.
It isn't.
The most basic of things, like tweening and movement aren't provided in the ways just about every other game engine on earth provides them.
So you need to download and setup a plugins and add-ons to get basic functionality.
Search for tweening engines for Unity, and read up about them, as you'll need to understand them and how they incorporate themselves into Unity for the next time you meet missing basics of Unity.
Quick disclaimer: I am not a very advanced C# user, being more accustomed to languages like python, so i apologise if the answer is right in front of me.
I've been making a little game for the Google Cardboard, Using the demo scene as a base. I've got some code that checks for a "Trigger Pull" and then should translate the Cardboard cameras up by 10 units.
//Checks For Magnet Trigger
if (Cardboard.SDK.Triggered)
{
//moves player up at a rate of 10u/s
transform.Translate(Vector3.up * 10);
Debug.Log("Triggered_Head");
//Tell Cardboard to maintain new position
}
Currently this works really well for detecting the magnet pull, and it does translate the cardboard. The problem is that almost immediately after translation the cardboard gets teleported back to the base position.
Currently i have this code inserted into the UpdateHead() method (?) from CardboardHead.cs, like so:
// Compute new head pose.
private void UpdateHead() {
if (updated) { // Only one update per frame, please.
return;
}
updated = true;
Cardboard.SDK.UpdateState();
if (trackRotation) {
var rot = Cardboard.SDK.HeadPose.Orientation;
if (target == null) {
transform.localRotation = rot;
} else {
transform.rotation = target.rotation * rot;
}
}
if (trackPosition) {
Vector3 pos = Cardboard.SDK.HeadPose.Position;
if (target == null) {
transform.localPosition = pos;
}
else {
transform.position = target.position + target.rotation * pos;
}
//Checks For Magnet Trigger
if (Cardboard.SDK.Triggered)
{
//moves player up at a rate of 10u/s
transform.Translate(Vector3.up * 10);
Debug.Log("Triggered_Head");
//Tell Cardboard to maintain new position
}
}
if (OnHeadUpdated != null) {
OnHeadUpdated(gameObject);
}
}
Doing this does everything right, but the location is reverted almost instantly (in the next frame i assume). So my question is: How do i make the transform stick, and is there a better way for me to handle this?
Alright, i figured out how to handle movement, and I'm posting the solution here for anyone in the future who can't figure it out. Quick note: I did update to the latest version of the SDK, although that shouldn't make a difference, except for naming.
So, Step 1:
Create an object to use as your controller. I just used one of the prototyping cubes from an asset pack, but whatever will work (empty objects would be best).
Place this object more or less in the center-point of the two cameras.
Parent your GvrMain object to the controller object (by dragging it onto the controller object) as well as any other components you want to move with the player (Guns, GUI's, etc)
Step 2:
Create a script for the controller object.
In the Update() method, add the lines:
if (GvrViewer.Instance.Triggered)
{
transform.Translate(Vector3.up)
}
for older versions replace GvrViewer.Instance with Cardboard.SDK
Customise your movement to your liking, any normal unity functions should work.
Some shortcomings:
You have to repeatedly press the trigger, using while() seems to break unity. This seems pretty easy to fix
The code snippet instantly translates up by 1. Not sure how to do it as a steady acceleration.
Hopefully this helps anyone who had my problem.
This script makes a cube "stick" to whatever it collided with. The problem is that when it's going at relatively high or medium speeds (or when the device itself is slow), the cube tends to "get a bit inside" what it collided with and then stick to it. What changes do I have to make to fix this?
In order for this script to work, one GameObject must have bool _sticksToObjects = true; and the other bool _sticksToObjects = false;
I have tried turning the Rigidbody's Collision Detection mode to either Continuous or Continuous Dynamic
I think my script depends on frame rate. That may be where the problem lies.
Normal "Attach":
Abnormal "Attach":
Rigidbody _rigidBody;
Transform _meshTransform;
bool _sticksToObjects = true;
public Transform _stuckTo = null;
protected Vector3 _offset = Vector3.zero;
void Awake()
{
GameObject CubeMesh = GameObject.FindWithTag ("CubeMesh");
GameObject Cube = GameObject.FindWithTag ("Cube");
_rigidBody = Cube.GetComponent<Rigidbody> ();
_meshTransform = CubeMesh.GetComponent<Transform> ();
}
void Update()
{
if (_stuckTo != null)
{
transform.position = _stuckTo.position - _offset;
}
}
void OnCollisionEnter(Collision collision)
{
if (!_sticksToObjects) {
return;
}
_rigidBody.isKinematic = true;
// Get the approximate collision point and normal, as there
// may be multipled collision points
Vector3 contactPoint = Vector3.zero;
Vector3 contactNormal = Vector3.zero;
for (int i = 0; i < collision.contacts.Length; i++) {
contactPoint += collision.contacts [i].point;
contactNormal += collision.contacts [i].normal;
}
// Get the final, approximate, point and normal of collision
contactPoint /= collision.contacts.Length;
contactNormal /= collision.contacts.Length;
// Move object to the collision point
// This acts as setting the pivot point of the cube mesh to the collision point
transform.position = contactPoint;
// Adjust the local position of the cube so it is flush with the pivot point
Vector3 meshLocalPosition = Vector3.zero;
// Move the child so the side is at the collision point.
// A x local position of 0 means the child is centered on the parent,
// a value of 0.5 means it's to the right, and a value of -0.5 means it to the left
meshLocalPosition.x = (0.5f * contactNormal.x);
_meshTransform.localPosition = meshLocalPosition;
if (_stuckTo == null || _stuckTo != collision.gameObject.transform) {
_offset = collision.gameObject.transform.position - transform.position;
}
_stuckTo = collision.gameObject.transform;
}
Here are some screenshots of the Unity editor:
This is a well-known category of problem in game engineering and you'll be pleased to know the solution is relatively simple. You'll be pleased to hear there are similar, but much more complicated, problems that are actually solved in the same way. I'll try to explain.
Now here's the thing. It's quite often that the following question comes up...
So I'm working on GTA. I have a humanoid, H, running around. She approaches vehicle V. She opens the door and gets in and drives off. After that everything goes to hell in Mecanim and all the code stops working. What to do?
Surprisingly, the way that is done in games is:
Surprisingly: you actually swap to totally different models at that point!!!!!
You have H and V in the game. But then you have an animation (say) for H climbing in to V. But then, you literally destroy the game objects of H and V, and you Instantiate (or just awake) a new, totally different, game object, which is D ("a car being driven around by a lady").
(If you think about it, you can see that when you do this, you carefully adjust all the stuff in D, so that it matches what was "just then happening" in the frame, in relation to both H and V. So for example, literally, you copy the transform, twist etc of the car V, to the new car-inside-D, if lady H has the SmearedMakeupEffect, you put the same SmearedMakeupEffect on the lady-within-D, you position all the bones identically, and so on.)
Another simple example of this is, you often get people asking, "my character C gets killed and I want it to become a ragdoll, how to?" In fact you just swap to a totally new game object you have all set up for that passage of the game. Indeed, if you have a character A ("Arnie") in a game, it's normal that you have 4 or 5 "different As" sitting offside the stage, so, there's "ragdoll A", "A who can dance" "A with weapon". And indeed many of these are combos, you know "A on the horse" "A in the car" and so on.
So interestingly, the "real" solution here is,
once they become a new connected thing, destroy them both and swap to a new game object altogether!
if you have made games "until you are blue in the face" from making games, this is just what you would do as a matter of course. Even though its' a simple situation, it's just easier in the long run. After all, consider all the stuff you have to do when this happens:
make hitting object child of the other
turn off physics on the child
change the way your physics works for the whole thing
turn off or change the collider on the hitting object, perhaps making it part of the overall object
you'll likely have some sort of new "separation" physics where it can be knocked-off - you'd have to turn all that on
likely change minor issues like sound effects, colors etc
As you can see it's a huge chore doing all this stuff, and indeed it's one of those things it's just "easier to do properly" and change to a new model.
All that being said, I know you want a Quick Script Solution you can Paste In :) Here it is...
Step 0, You'll create "YourScript" which goes on the "main" cube. it will "catch" another cube moving around.
YourScript will look basically like this ...
[System.NonSerialized] public bool isConnectedNow;
void OnCollisionEnter(Collision collision)
GameObject theThingWeCaught = collision.gameObject
Debug.Log("We caught this thing .. " + theThingWeCaught.name)
// make it a child of us......
theThingWeCaught.transform.parent = transform
theThingWeCaught ... set kinematic
theThingWeCaught ... probably disable the rigidbody
theThingWeCaught ... probably disable the collider
isConnectedNow = true;
That's really all you have to do.
Step 1, YOUR script must have a public bool like this
[System.NonSerialized] public bool isConnectedNow;
Step 2, Here's MyScript which goes on the hitting cube, first we'll unit-test that your isConnectedNow bool is working
public Class MyScript:MonoBehaviour // attach to the "child" cube
{
public float correctXDistance;
public float correctYDistance;
public Transform bigCube;
public YourScript yourScript;
void Update()
{
string message = yourScript.isConnectedNow ? "free" : "stuck";
Debug.Log("I am " + message);
}
}
attach, debug, and run. Make the little cube stick and unstick from the big cube .. Watch the console. it works? So add this to MyScript
private void DistanceCorrectionX()
{
float xDistance = bigCube.position.x - transform.position.x;
float xSign = Mathf.Sign(xDistance);
float xDelta = Mathf.Abs(xDistance);
float closenessPercentage = (xDelta/correctXDistance)*100f;
if ( closenessPercentage<90f || closenessPercentage>110f)
{
// they are not close enough to quantize on this axis
// this comes in to play when you have multiple axes
return; // do nothing.
}
float xShouldBe = bigCube.position.x + xSign * correctXDistance;
Vector3 p = transform;
p.x = xShouldBe; // be careful it's .y, .z etc for other axes
transform.position = p;
}
for now call that in Update() in MyScript like this
void Update()
{
Debug.Log("I am " yourScript.isConnectedNow ? "free" : "stuck");
if (yourScript.isConnectedNow) DistanceCorrectionX();
}
Now actually Play and make it stick. Now, since it's running in Update simply while Play look at the Inspector for MyScript and adjust the value of correctXDistance to get the exact look you want. When yo have decided on a value, unPlay and put that in as the final value you wish.
Next, in DistanceCorrectionX simply duplicate all the code and do it again for the Y axis DistanceCorrectionX. If you also do Z, do that.
Finally. Note you will have a lot of messy code, like this...
void Update()
{
// handle all the DistanceCorrectionX etc as seen above.
if (yourScript.isConnectedNow)
{
.. turn off the collider on me
}
else
{
.. turn on the collider on me
}
}
and so on, there's "many little things" you'll need to do.
Don't forget also, overwhelmingly you may want to make the hitting object a child of the big object, depending on your situation. (Then of course they would move around together as a unit.)
Note that in the positioning code above I just showed it as position, not local position, for pedagogic clarity. If you want to do them flinging around, and spinning and so on, you'd make the hitting object a child of the other and you would use localPosition in the same way. Enjoy.
One possible way that comes to my mind is:
Inside of the "collision enter" check the distance between these objects and move the one that should stick to the other one a bit away.
As you see in the picture the distance between A and B should be equal to the sum of the widths divided by 2 (with a small threshold of course).
If the distance is less than the sum of the widths / 2 then you have an abnormal "attach" and you have to move one of the objects away. Its not really difficult to accomplish that.