AddForce Method Not Working - c#

I am programming a top-down hack-and-slash game for a class. We want a mechanic where if you get hit by an enemy, or an enemy hits you, you get 'knocked back'. Unfortunately, no matter what I have tried, I cannot get either the enemy or the player to react to the force.
Here is a list of things I have checked that have been suggested in other questions like this:
Player/Enemy is NOT Kinematic
Tried with both Gravity on and off
No positions/rotations are frozen
Player/Enemy have Rigidbodies attached
Player/Enemy have colliders attached, one with and one without the 'isTrigger' function checked.
Tried both OnCollisionEnter and OnTriggerEnter
The force value is high, the mass, drag, and angular drag are low
I have run out of ideas. Any and all support you can give is greatly appreciated.
Here is a snippet of the code from a script from the player Object:
public void OnTriggerEnter(Collider col)
{
if (col.gameObject.tag == "EnemyHit" && !invincible)
{
Debug.Log("The player has been hit!");
//sets player as invincible
invincible = true;
// Set the damaged flag so the screen will flash.
hit = true;
timeOfHit = Time.time;
// Reduce the current health by the damage amount.
currentHealth -= 1;
GetComponent<Rigidbody>().AddForce(transform.forward * recoilThrust, ForceMode.Force);
Debug.Log("This is the force that is being added to the player when it is hit. : " + -transform.forward * recoilThrust);
//...
}
}
I can prove (using the Debug.Log) function, that the code reaches there, and the force is calculated.

To summarise the comments:
Rigidbody.AddForce doesn't work when the object has a CharacterController besides the rigidbody. In this case the effect has to be "faked". Possible ways can be found here:
The last couple of posts
Another solution, C# and Javascript
Basically you need to apply the force using CharacterController.Move.

Is Kinematic enabled? Doing so will make the object ignore physic forces.

Related

Child Objects Not Following Parent Object On Frequent Teleportation

Game Screen
Teleportation Code
Child Objects And Their Original Location
I'm new to Unity, and so after taking just some regular online courses for Unity2D I wanted to mess around with adding in different features, the first of which I decided to do was something like in Portal, where a projectile spawns two connected portals you can teleport between. However, I've run into an issue. When I'm teleporting my character, sometimes, usually when I'm teleporting too quickly but can happen at any time, the child objects to the Player game object tend to shift, and I don't understand why. **I'd like to:
Know why the offset between the parent and child objects are changing through teleportation.
Know how to fix this issue, preferably in code I can easily understand as a beginner to Unity. Also preferably in a way that doesn't involve me constantly appending the child objects to the parent object through transform position with the added offset, though if it's the simplest solution I'm not against trying it.**
Something worth noting is that the offset change is different as well, I have a Child Object called Feet which detect the Ground for jumping, which seems to remain at the location of the previous portal when it first breaks. However, another child object called Gun which is where the projectiles spawn from seem to only move down a little bit, meaning there's inconsistency in how they are offset when they break. It might be because the Feet has a collider, but I'm unsure, don't know enough, and only felt it was worth mentioning.
[SerializeField] GameObject otherPortal;
Portal otherPortalComponent;
BoxCollider2D boxCollider2D;
bool firstEntered = true;
// Start is called before the first frame update
void Start()
{
boxCollider2D = GetComponent<BoxCollider2D>();
otherPortalComponent = otherPortal.GetComponent<Portal>();
}
private void OnTriggerEnter2D(Collider2D collision)
{
if (boxCollider2D.IsTouchingLayers(LayerMask.GetMask("Player")) && firstEntered)
{
Teleport(collision.gameObject);
otherPortalComponent.SetFirstEnteredFalse();
}
}
private void Teleport(GameObject obj)
{
obj.transform.position = otherPortal.transform.GetChild(0).transform.position;
}
public void SetFirstEnteredFalse()
{
this.firstEntered = false;
}
private void OnTriggerExit2D(Collider2D collision)
{
this.firstEntered = true;
}
To simplify the question, the position of the child objects relative to the parent changes when I instantly change the parents position sometimes, why does this happen and how do I fix the issue without simply using transform.position in an Update method to constantly append the child to the parent, if possible.
I would look at your OnTriggerEnter2D method's if statement for a solution.
private void OnTriggerEnter2D(Collider2D collision)
{
if (boxCollider2D.IsTouchingLayers(LayerMask.GetMask("Player")) && firstEntered)
{
Teleport(collision.gameObject);
otherPortalComponent.SetFirstEnteredFalse();
}
}
Because you are using OnTriggerEnter this method will only be called with your feet since your player's main collider isn't a Trigger. This means that your collision variable that you call your Teleport method on is actually your feet object, not your player's body object. So you are changing the offset of your feet from your player in your Teleport method.
I would try changing your collision method to OnCollisionEnter2D(Collision2D)
which would pick up your player's base collider when it enters and not the feet collider.
void OnCollisionEnter2D(Collision2D collision)
{
if (collision.gameObject.GetComponent<Player>() && firstEntered)
{
Teleport(collision.gameObject);
otherPortalComponent.SetFirstEnteredFalse();
}
}
Since the Player script is on the same object as your base player collider you can do a simple GetComponent<Player>() call to check if its the player object. Though you could still use the Layer to check if you want.

Falling Blocks on player touch - Unity (C#)

I'm making a game in unity for a school project. I'm stuck on this one part where I want a block to fall and be destroyed once a player has touched the block and moved to the next. Having so much trouble and would love some assistance.
The concept of what i'm aiming for can be seen here: http://www.coolmath-games.com/0-b-cubed
On each block, you'll need to attach a script which contains the OnCollisionExit () method (reference). Pass in the collision parameter (see reference) and use the collision info to confirm that the object leaving the collider is the player (eg tag the player with a player tag in the inspector and check the collision's collider's tag).
In this method, place your code for making the block fall.
Make sure you've added colliders to your objects so that they interact. If you run into problems, post back some code and I'll get back to you.
Actually you do not need to detect collision here. It is not neccessary. Just compare base cube position with player cube position in x,z plane (there should be only difference in Y-axis since player cube is over base cube). No need for collsions here. Than you attach script to all base cubes checking for playercube to hover other them (position check) and than on playercube next movement you attach rigidbody to basecube and destroy it after a second. Simple:)
EDIT
The code should look like this. More or less.
GameObject playerCube; //this is reference to Player object
bool playerEnter = false;
bool playerLeft = false;
void Start()
{
playerCube = Gameoject.Find("PlayerCube"); // here you put the name of your player object as a string. Exactly as it is in the hierarchy
}
void Update()
{
if(playerCube.transform.position.x == transform.position.x && playerCube.transform.position.z == transform.position.z)
{
playerEnter = true; // this checks if player stepped on the cube
}
if((playerCube.transform.position.x != transform.position.x || playerCube.transform.position.z != transform.position.z) && playerEnter == true && playerLeft == false) //checks if player left the cube
{
playerLeft = true; // we do this so the code below is executed only once
gameObject.AddComponent<Rigidbody>(); // ads rigidbody to your basecube
Destroy(gameObject, 1.0f); //destroys baseCube after one second
}
I think that more or less that should do it. In the final game I probably would use coroutines for your task. Also you need to think how do you check if the cube already was destroyed, I mean how will your game detect if player can step on the next cube - does it exist or not?
EDIT 2
Hard to tell what will work or not without your hierarchy, your code and knowing what do you want to have exactly. Study this code - it will work after some adjustments to your needs

Find and add the velocity of a Rigidbody that's directly below another? (Unity + C#)

I have an issue that I've not been able to solve for a few days now and without it working I can't move on with my project, so your help would be greatly appreciated!
What I'm trying to do is 'find' the velocity of an object that is directly below my 2D sprite (that also contains a Rigidbody and 2D box collider) and then add that velocity (in the same direction) to the object that is 'looking' for it.
I feel like ray-casting might be part of the answer but I'm not sure how to use that, especially in this context.
The idea behind this is I have a platform that can carry objects stacked on top of each other, so you move the mouse, it manipulates the platforms velocity, but this causes the objects on-top to fly backwards, no mater how high the friction is.
Here is the platform code:
void Update()
{
float distance_to_screen = Camera.main.WorldToScreenPoint(gameObject.transform.position).z;
Vector3 pos_move = Camera.main.ScreenToWorldPoint(new Vector3(Input.mousePosition.x, Input.mousePosition.y, distance_to_screen));
mouseDelta = pos_move - lastMousePosition;
acceleration = mouseDelta.x * 0.4f;
platformVelocity += acceleration;
platform.velocity = new Vector2(platformVelocity, 0) * speed;
lastMousePosition = pos_move;
}
Thank you for taking the time to read this, I would very much appreciate any help you can give!
Is it necessary to 'find' that object? 'Finding' may be difficult & costing.
Can you hold the 'below-thing' reference and pass it to 'above-thing'?
I'm assuming you have some method on objects that checks whether it's on a moving platform. One option is to add an IsOnMovingPlatform flag on those objects. Then use a similar method to check if any object is on top of the "IsOnMovingPlatform == true" objects. Keep doing passes on that method until you don't come up with any more flags set to true.
So, i'm trying to understand your question and I think I might know what you're talking about.
Are you saying that you want your game object (2d sprite) to stay on the platform as its moving? I have had the same problem, where a platform is moving and my player (game object) slides off- and this is how I fixed it.
Add a box collider to the platform (trigger). When the player enters the platform, make the player's transform the platform. That way, when the platform moves- the player moves with it no matter what the speed is.
For example:
void OnTriggerEnter2D(Collider2d collider){
if(collider.tag.equals("Player")){ // you only want the player to work
// attach script to platform for "this.transform" to work
collider.transform.SetParent(this.transform);
}
}
then, to unparent the transform "otherwise, even when the player is off the platform, it will move with the platform.
void OnTriggerExit(Collider2d collider){
// assuming your player isn't supposed to have a parent!
collider.transform.parent = null;
}
Sorry if I misunderstood your question! If this is what you were going for, finding the velocity of the platform is not necessary. Also, this is uncompiled code- if you have issues let me know.
EDIT:
To add a bit of a give, for the entire duration that the player is in contact with the platform, we can add a little bit of a negative force to shove the player a little bit!
We do this by using OnTriggerStay2D (assuming your game is 2d, otherwise use OnTriggerStay).
For example:
void OnTriggerStay2D(Collider2d collider){
if(collider.tag.equals("Player")){
collider.getComponent<Rigidbody2D>().addForce(new vector2(forceToAdd, 0));
}
}
now, that "forceToAdd" variable should be based on the speed and direction that your platform is going. Does your platform have a rigid body on it? if so you can get your force variable by retrieving the rigid body from the platform (getcomponent()) and get the X velocity from it. With this velocity, experiment with dividing it by numbers until you find a good fit, multiply it by -1 (because you want to push the player in the opposite direction of the platform travel), then this will be your forceToAdd!
However, if your platform is not using a Rigidbody, then a way you can do this is record the x positions in your script (just two positions, and subtract these to find the distance in an update method), multiply by -1, try dividing by a number (experiment with this) and that can be your force to add variable.
Also, my code for the OnTriggerStay2d is rough, the spelling could be a bit wrong- but this will give you a rough idea for a solution! ALSO.. I don't think getting the component of a rigid body should be done in every frame, a way around this would to have your rigid body component as a global variable in the script, then set your rigid body on OnTriggerEnter2d, this way it will only retrieve it once (instead of every frame) which I assume would be more efficient!
Good luck! and if you have any questions il try to help!
This is a raycast2d version, I tested it and it works much better than the trigger collider version :).
using UnityEngine;
using System.Collections;
public class RaycastFeetDeleteLater : MonoBehaviour {
[SerializeField] private float raycastDist = 0.1f;
[SerializeField] Transform feet;
[SerializeField] private float xForceDampener = 0.1f;
private Rigidbody2D rb2d;
// Use this for initialization
void Start () {
rb2d = GetComponent<Rigidbody2D> ();
}
// Update is called once per frame
void FixedUpdate () {
RaycastDown ();
}
private void RaycastDown(){
RaycastHit2D hit = Physics2D.Raycast (feet.position, Vector2.down, raycastDist);
if (hit.collider != null) {
print (hit.transform.tag);
if (hit.collider.tag.Equals ("Platform")) {
transform.SetParent (hit.transform);
Vector2 force = new Vector2 ((hit.transform.gameObject.GetComponent<Rigidbody2D> ().velocity.x * xForceDampener) * -1, 0);
print (force);
rb2d.AddForce (force);
} else if (transform.parent != null) {
print ("UNPARENT");
transform.parent = null;
}
} else {
if (transform.parent != null) {
transform.parent = null;
}
}
Debug.DrawRay (feet.position, Vector3.down, Color.green, raycastDist);
}
}
This script is attached to the player! No need to have scripts on the platforms, which was the main reason my previous method was a pain :P
Basically this script does the following:
- Gets rigidbody2d of the player on start (we will be adding forces to the player with it soon, so might as well store it in a variable!
On FixedUpdate (Different than Update, FixedUpdate is called on Physics update, where as Update is called every frame) I have read that raycast should use FixedUpdate, I suppose because it is a Physics method!
RaycastDown shoots a 2D raycast directly down from the feet position (to get the feet, create a child object of the player, put the position of the feet a tiny bit below the player, NOT ON THE PLAYER- I will explain why in a second why that will mess up everything! Then drag the feet GameObject on to the "feet" slot of the script.)
Raycast shoots from feet directly down at a distance that is changeable in the editor (raycastDist) I found a distance of 0.1f was perfect for my needs, but play around with it for yours! You can actually see a drawing of your Ray by using Debug.DrawRay as seen in the code, the ray is only visible in the editor- not game view! But this will help, especially with positioning the feet.
Checks if it hit an object with a collider, if so check if the objects tag is a Platform (set the platforms to a new tag and call it Platform).
If it hits a platform set the parent of the player to the platform (like the last script) and start applying a little force to the player in the negative direction that the platform is traveling (we get the rigid body of the platform to find its velocity).
If the raycast does not hit a platform, unparent it so the player can move freely.
Thats basically it, however...
Please note:
This script needs to be on the player, not any of the players child objects, since this script will unparent the player from any platforms- if you have the script on a child object of the player it will unparent from the player- which is horrible. So The script MUST be on the root game object of the player. There are ways around this if for some reason you can't.
As mentioned before, the "feet" child object must be a little bit under the player- this is because when the Raycast2d "fires" it will find the FIRST object with a collider it hits- so if you have the start of the raycast hit a collider on the player then it will always show the raycast hitting the player, not a platform. So to get around this have the raycast fire from the feet- and have the feet position a tiny bit below the player. To help with this I added a print statement that will print to the console which object the raycast is hitting, if it doesn't say "Platform" then you need to lower the feet! :)
Furthermore, I'm not sure how you are moving your platforms- but if the Velocity of the platform always prints "0,0" then its because you are moving the platforms position directly, instead of moving it by forces (this happened to me when I was testing this script). I got around this by moving the platform by "AddForce". Also depending on the "Mass" of your platforms, you may need to drastically increase the "xForceDampener" which I should of actually labeled "xForceMultiplier" as I found while testing I needed to set it much higher- like at 60, instead of 0.25 which I previously thought would be okay..
Hope this helped :D
EDIT: Linking the unity docs for 2D Raycasts in case you need
http://docs.unity3d.com/ScriptReference/Physics2D.Raycast.html

OnCollisionEnter2D fails to send information

I am testing OnCollisonEnter2D and it doesn't seem to want to work for me.
I have box collider 2d and rigidbody2d's on both of my game objects and again, it fails to send a message to the console.
using UnityEngine;
using System.Collections;
public class CollisionAndResetSystem : MonoBehaviour {
void OnCollisionEnter2D(Collision2D coll) {
if (coll.gameObject.tag == "Cube")
print ("hit");
}
}
I took this strait from the Scripting API and it doesn't want to work.
There is no problem in the script. Problem must be in your "boxcollider2d" component. Please check the size of "boxcollider2d", change it to something like 100 in both x and y and in both the gameobjects also. Please check in the scene view if you can see the "boxcollider2d" gizmo. You have to make "boxcollider2d" as big as your gameobjects, then only OnCollisionEnter2D will work.
If you dont need your objects to be affected by physics
then set isKinematic = false and Gravity Scale = 0 and remember to attach your OnCollisionEnter2D script to your gameObject
A Couple of things:
void OnCollisionEnter2D(Collision2D coll) {
if (coll.gameObject.tag == "Cube")
print ("hit");
}
Make sure you are comparing with a correct tag (keep the case sensitivity in mind).
Make sure the gameObject that is being compared has this tag linked. May be you defined the tag but there is no gameObject with this tag.
Check the size of your collider component, it should be enough big to receive touch event.
Hope it helps!
Most important is that check on which gameobject your script is attached,Cross check that your script are attached to your gameobject that you are trying get collision with. And Use Debug.log() too in place of Print(),Hope this help.

Kinematic Rigidbody moves on its own

I have a very weird issue in my 2D Unity game, which I was able to reduce to the following core problem/minimal reproducing test case. Follow these steps to reproduce (Unity 5.1.1f1):
Create a player object (Cube) at location (0,0,0).
Remove the BoxCollider Component.
Attach the following C# script, Unity will automatically add the required Components and thereby make it a Rigidbody Collider.
Set the isKinematic flag.
Add another Cube to the scene at location (2,0,0).
Remove the BoxCollider Component and add a BoxCollider2D. This makes this cube a static Collider.
Set the isTrigger flag.
Run the scene.
Expected behavior:
The player cube accelerates towards the other cube and stops moving once it touches it.
Observed behavior:
The player cube accelerates towards the other cube, then continues moving at constant speed.
Additional implementation details:
I originally moved all objects by translating their transform and didn't use Rigidbodies at all because I didn't need collision detection. Now I do, so I want Rigidbodies. I dived into online resources and found out I'm supposed to use rigidbody.MovePosition() rather than transform.Translate() or transform.position. I changed my script, and the above error appeared.
Going back to transform.position fixes the issue, but that's not a good solution, as it involves bad practice which, according to what I read, produces significant CPU loads.
Failed attempts to solve:
Switching to Update() and Time.deltaTime didn't make any difference.
I tried not returning in the Update() and instead simply resetting the timestep to 0 while stop is set. No change.
Fiddling with the Inspector and doing stuff like freezing position on the rigidbody or setting the player objects to also be a trigger had no effect at all. Changing anything on the Rigidbody component while the game is running (after the collision) makes the cube stop immediately. Literally anything, even setting its mass to 0.
I also tried setting velocity to 0, resulting in no change. Which does make sense since Update() is skipped entirely (I also checked that with a Debug.Log() by the way).
So at this point, I'm down to barely 30 lines of code and I still have no idea what's causing this. Since the involved objects are a static Trigger collider and a kinematic rigidbody collider, both with no physics materials, there should be nothing that makes this thing move once the flag is set. But it does move.
SimpleController2D.cs
using UnityEngine;
using System.Collections;
[RequireComponent (typeof (BoxCollider2D), typeof (Rigidbody2D))]
public class SimpleController2D : MonoBehaviour {
public Vector3 velocity = Vector3.zero;
private Transform thisTransform;
private Rigidbody2D thisRigidbody;
public bool stop = false;
void Awake () {
thisTransform = GetComponent<Transform> ();
thisRigidbody = GetComponent<Rigidbody2D> ();
}
void FixedUpdate() {
float timestep = Time.fixedDeltaTime; // temporarily stored for ease of access
if (stop) {
return; // freeze on hit
}
velocity.x += timestep; // accelerate
/* add a second slash (/) to toggle between transform and rigidbody
thisTransform.position += velocity * timestep; /*/
thisRigidbody.MovePosition ((Vector3)thisRigidbody.position + velocity*timestep); //*/
}
void OnTriggerEnter2D(Collider2D col) {
stop = true;
}
}
Solution
This is a bug in Unity 5.1.1f1 and was fixed in the patch release 5.1.1p2 and later.
Get it here:
http://unity3d.com/unity/qa/patch-releases?version=5.1
What happened?
You can even reduce the problem to a single MovePosition call. MovePosition uses the physics engine to move the object. Therefore Unity calculates the velocity necessary to reach the target position within the next physics update. Version 5.1.1f1 fails to reset the velocity to zero after reaching the position, so the object will just continue moving with the calculated velocity.

Categories