Reputation: 3842
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)
So this is the look of my walls in the 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.
Upvotes: 0
Views: 1524
Reputation: 7615
This was a hard one. But here is my solution:
Mass = 10
and for the character Mass = 1
The Empty GameObject have a trigger collider, to detect when the Player jumps over the platform. When that happens two actions take place:
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:
Here the inspector for the Empty GameObject
Here the inspector for the Platform:
Here the inspector for the Player:
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
Upvotes: 1
Reputation:
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.
Upvotes: 1