0
votes

I want to create a pushing wall in a 3D game. Here's an example of pushing walls in Super Mario 64

Pushing Walls in Super Mario 64 (Video on Youtube)

Pushing Wall

So this is the look of my walls in the inspector

Inspector

And this is the code attached to the pushing wall

public class PushingObstacle : MonoBehaviour {

    private Vector3 startPoint; // the current position when loading the scene

    [SerializeField]
    private Vector3 endPoint; // the target point

    [SerializeField]
    private float forwardSpeed; // speed when moving to the end

    [SerializeField]
    private float backwardSpeed; // speed when moving back to start

    float currentSpeed; // the current speed (forward/backward)

    private Vector3 direction; // the direction the wall is moving
    private Vector3 destination; // the target point

    private Rigidbody obstacleRigid; // rigidbody of the wall

    private void Start()
    {
        startPoint = transform.position;
        obstacleRigid = GetComponent<Rigidbody>();
        SetDestination(endPoint); // set the target point
    }

    private void FixedUpdate()
    {
        obstacleRigid.MovePosition(transform.position + direction * currentSpeed * Time.fixedDeltaTime); // start moving

        if (Vector3.Distance(obstacleRigid.position, destination) < currentSpeed * Time.fixedDeltaTime) // set a new target point
            SetDestination(destination == startPoint ? endPoint : startPoint);
    }

    private void SetDestination(Vector3 destinationPoint)
    {
        destination = destinationPoint;
        direction = (destination - transform.position).normalized; // set the movement direction
        currentSpeed = destination == endPoint ? forwardSpeed : backwardSpeed; // set the speed
    }
}

So when the player moves to the wall and touches it just a little, the wall just flies away.

I could set the walls rigidbody on kinematic mode. But I want the wall adds a minimal force to the player when pushing him. How can I achieve both behaviours (not flying away and small force against the player)?

Edit:

And I can not set it to kinematic because when jumping on the top of the wall (cube), the player is not moved relative to the wall.

2
you can increase the wall's rigidbody massLestat
What is the player's mass versus the wall's mass?user7618628
both of them got the default massQuestion3r
As @Lestat stated, try changing your wall's mass to a higher value, or lowering your player's mass.user7618628

2 Answers

1
votes

This was a hard one. But here is my solution:

  • To make the Pushing Wall be able to push the Player it need a larger Mass than it. In my case I set for the wall Mass = 10 and for the character Mass = 1
  • The Pushing Wall is be Kinematic to don´t be affected by collisions with the GameObject.
  • The Pushing Wall is a child of an Empty GameObject which will move it forward-back in an infinite loop. We need an Empty GameObject so when the child jump over the Platform (And becomes a child of it) its scale is not affected.
  • The Empty GameObject have a trigger collider, to detect when the Player jumps over the platform. When that happens two actions take place:

    1. The Player becomes a child of the Empty GameObject and moves
      along with the Platform, but at the same time can still be controlled with the keyboard inputs.
    2. The Player becomes kinematic to move along with the Plataform
  • When the player leaves the top of the plataform it stops being a child of the Emtpy GameObject and stops being kinematic

    Here is an image of the scene with the Empty GameObject selected, to show the collider on top of the platform:

enter image description here

Here the inspector for the Empty GameObject

enter image description here

Here the inspector for the Platform:

enter image description here

Here the inspector for the Player:

enter image description here

And Finally the scripts.

Script for the Empty GameObject (To detect if the player is over the platform and to move the platform between two Empty GameObjects placed in the scene to determine the path of the Platform)

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEditor;


public class Push : MonoBehaviour {

    public Transform target;
    public Transform end;

    bool movingForward = true;

    float speed = 9f;

    bool moving = false;

    // Update is called once per frame
    void Update () {

        if(movingForward)
         {
            //transform.position = new Vector3(transform.position.x + 0.001f, transform.position.y, transform.position.z);
            float step = speed * Time.deltaTime;
            transform.position = Vector3.MoveTowards(transform.position, target.position, step);

            if(transform.position.Equals(target.position))
                movingForward = false;
         }else{
            float step = speed * Time.deltaTime;
            transform.position = Vector3.MoveTowards(transform.position, end.position, step);

            if(transform.position.Equals(end.position))
                movingForward = true;
         }

    }

    void OnTriggerEnter(Collider other)
    {
        print("Something Inside");
        if(other.tag == "Player")
        {
            print("It is the player");
            other.gameObject.GetComponent<Rigidbody>().isKinematic=true;
            other.gameObject.transform.parent = this.transform;
        }
    }

    void OnTriggerExit(Collider other)
    {
        if(other.tag == "Player")
        {
            other.gameObject.GetComponent<Rigidbody>().isKinematic=false;
            other.gameObject.transform.parent = null;
        }
    }

}

Now the Scrip to move the Player

using UnityEngine;

public class PlayerMovement : MonoBehaviour
{
    public float speed = 6f;
    Vector3 movement;
    Rigidbody playerRigidbody;
    int floorMask;
    float camRaylength = 100f;
    float JumpSpeed = 15f;

    void Awake()
    {
        floorMask = LayerMask.GetMask("Floor");
        playerRigidbody = GetComponent <Rigidbody> ();
    }
    void FixedUpdate()
    {
        float h = Input.GetAxisRaw ("Horizontal");
        float v = Input.GetAxisRaw ("Vertical");

        Move (h, v);
        Turning ();
        if(Input.GetKeyDown(KeyCode.Space))
            Jump();

    }

    void Jump()
    {
        playerRigidbody.isKinematic=false;
        playerRigidbody.AddForce(Vector3.up *JumpSpeed);

    }

    void Move(float h, float v)
    {
        movement.Set(h, 0f, v);

        movement = movement.normalized * speed * Time.deltaTime;

        playerRigidbody.MovePosition (transform.position + movement);
    }

    void Turning()
    {
        Ray camRay = Camera.main.ScreenPointToRay (Input.mousePosition);

        RaycastHit floorHit;

        if (Physics.Raycast (camRay, out floorHit, camRaylength, floorMask)) {

            Vector3 playerToMouse = floorHit.point - transform.position;
            playerToMouse.y = 0f;

            Quaternion newRotation = Quaternion.LookRotation (playerToMouse);
            playerRigidbody.MoveRotation (newRotation);

        }
    }

    void Animating(float h, float v)
    {
        bool walking = h != 0f || v != 0f;

    }

}

Test it and let me know if anything is not clear

1
votes

I believe your issue is being cause by the mass of the player versus the mass of your wall. Ensure these values are semi-realistic with relationship to each other. If your wall has a mass of 1, then try setting your player's mass to 0.1 since obviously a concrete wall is much heavier than your average organic life form.

Also, the Unity forums tend to have better results for these types of issues. I don't believe it's an issue with your code, just an issue with values being supplied to the objects being used.