I’m fiddling with a “walk on walls” code.
I’ve made it so the player can jump off the wall, and will drop to the ground (basically… if its holding on to the surface “touching” it’ll stay oriented on the face of the wall) but if you jump, then gravity takes hold, reorients you to have your normal aligned with the world.
I can get it to work “if” I hit the button twice.
Then… there’s the double (and in this case… unlimited) jumping aspect… that I do not want. If I’m on the normal ground, I can repeatedly hit the jump button and I climb higher and higher. My code has a bool that basically toggles… so “jumping = false” normally, but when I hit the jump button, it calls a “jump off” method that has jumping = true; That method calls another method that is Ienumerator, and then makes jumping = false…
So… why is the button not disabled while I’m jumping? (my input command is
if (Input.GetButtonDown && !jumping){
JumpOffWall();
}
but regardless of my location in space, on ground, on wall… I can repeatedly hit the jump button and climb higher and higher. If I jump off a wall just once I fall back to the wall without orienting…(the code has it jump in the “myNormal” direction then reorient on the ground) but hitting it the second time runs the full code for reorienting… and falling… but then I can hit button again and again to climb higher… again
here is the code I’m currently using (I got it from
Basic Movement: Walking on Walls
I have tweaked it a little bit…
using UnityEngine;
using System.Collections;
using System.Collections.Generic;
/// <summary>
/// C# translation from http://answers.unity3d.com/questions/155907/basic-movement-walking-on-walls.html
/// Author: UA @aldonaletto
/// </summary>
// Prequisites: create an empty GameObject, attach to it a Rigidbody w/ UseGravity unchecked
// To empty GO also add BoxCollider and this script. Makes this the parent of the Player
// Size BoxCollider to fit around Player model.
public class QM_CharController : MonoBehaviour {
private float moveSpeed = 6; // move speed
private float turnSpeed = 90; // turning speed (degrees/second)
private float lerpSpeed = 10; // smoothing speed
private float gravity = 10; // gravity acceleration
private bool isGrounded;
private float deltaGround = 0.2f; // character is grounded up to this distance
private float jumpSpeed = 10; // vertical jump initial speed
private float climbRange = 5; // range to detect target wall
private Vector3 surfaceNormal; // current surface normal
private Vector3 myNormal; // character normal
private float distGround; // distance from character position to ground
private bool jumping = false; // flag "I'm jumping to wall"
private float vertSpeed = 10; // vertical jump current speed
private bool climbing = false;
private Transform myTransform;
public BoxCollider boxCollider; // drag BoxCollider ref in editor
private void Start(){
myNormal = transform.up; // normal starts as character up direction
myTransform = transform;
rigidbody.freezeRotation = true; // disable physics rotation
// distance from transform.position to ground
distGround = boxCollider.extents.y - boxCollider.center.y;
}
private void FixedUpdate(){
// apply constant weight force according to character normal:
rigidbody.AddForce(-gravity*rigidbody.mass*myNormal);
}
private void Update(){
// climb code - jump off wall or simple jump
if (climbing) return; // abort Update while climbing to a wall
if (jumping)return; // abort Update while jumping
Ray ray;
RaycastHit hit;
if (Input.GetButtonDown("Climb")){ // climb pressed:
ray = new Ray(myTransform.position, myTransform.forward);
if (Physics.Raycast(ray, out hit, climbRange)){ // wall ahead?
ClimbToWall(hit.point, hit.normal); // yes: climb on the wall
}
}
if(Input.GetButtonDown ("Jump") && !jumping){ // no: if grounded, jump up
JumpOffWall ();
}
// movement code - turn left/right with Horizontal axis:
myTransform.Rotate(0, Input.GetAxis("Turn")*turnSpeed*Time.deltaTime, 0);
// update surface normal and isGrounded:
ray = new Ray(myTransform.position, -myNormal); // cast ray downwards
if (Physics.Raycast(ray, out hit)){ // use it to update myNormal and isGrounded
isGrounded = hit.distance <= distGround + deltaGround;
surfaceNormal = hit.normal;
}
else {
isGrounded = false;
// assume usual ground normal to avoid "falling forever"
surfaceNormal = Vector3.up;
}
myNormal = Vector3.Lerp(myNormal, surfaceNormal, lerpSpeed*Time.deltaTime);
// find forward direction with new myNormal:
Vector3 myForward = Vector3.Cross(myTransform.right, myNormal);
// align character to the new myNormal while keeping the forward direction:
Quaternion targetRot = Quaternion.LookRotation(myForward, myNormal);
myTransform.rotation = Quaternion.Lerp(myTransform.rotation, targetRot, lerpSpeed*Time.deltaTime);
// move the character forth/back with Vertical axis:
myTransform.Translate(0, 0, Input.GetAxis("Forward")*moveSpeed*Time.deltaTime);
}
private void ClimbToWall(Vector3 point, Vector3 normal){
// climb to wall
climbing = true; // signal it's jumping to wall
rigidbody.isKinematic = true; // disable physics while climbing
Vector3 orgPos = myTransform.position;
Quaternion orgRot = myTransform.rotation;
Vector3 dstPos = point + normal * (distGround + 0.5f); // will jump to 0.5 above wall
Vector3 myForward = Vector3.Cross(myTransform.right, normal);
Quaternion dstRot = Quaternion.LookRotation(myForward, normal);
StartCoroutine (jumpTime (orgPos, orgRot, dstPos, dstRot, normal, 0.4f));
//jumptime
}
private void JumpOffWall(){
// jump off wall
jumping = true;
Vector3 orgPos = myTransform.position;
Quaternion orgRot = myTransform.rotation;
Vector3 myForward = Vector3.Cross(myTransform.right, Vector3.right);
Quaternion dstRot = Quaternion.LookRotation(myForward, Vector3.up);
StartCoroutine (jumpTime (orgPos, orgRot, orgPos, dstRot, Vector3.up, 0.2f));
//jumptime
}
private IEnumerator jumpTime(Vector3 orgPos, Quaternion orgRot, Vector3 dstPos, Quaternion dstRot, Vector3 normal, float time) {
rigidbody.velocity += jumpSpeed * myNormal;
for (float t = 0.0f; t < time; ){
t += Time.deltaTime;
myTransform.position = Vector3.Lerp(orgPos, dstPos, t);
myTransform.rotation = Quaternion.Slerp(orgRot, dstRot, t);
yield return null; // return here next frame
}
myNormal = normal; // update myNormal
rigidbody.isKinematic = false; // enable physics
climbing = false; // climbing is accomplished
jumping = false;
}
void OnTriggerEnter(Collider other){
Ray ray;
RaycastHit hit;
if (other.gameObject.tag == "tree") {
ray = new Ray (myTransform.position, myTransform.forward);
if (Physics.Raycast (ray, out hit, climbRange)) { // wall ahead?
ClimbToWall (hit.point, hit.normal); // yes: climb on the wall
}
}
}
}