- Home /
Keeping A Character On The Terrain (Allow no Y movement)
Hello!
I am working on an RTS game with a top-down orthographic camera.
I have the click to move functionality working except for 1 hiccup. I cannot get the character to stay on a height level of 1 only.
Basically I have the character trapped inside a crater and I want him to have to stay in the crater. However when I click to move anywhere he always moves UP the terrain (even if I try clamping the Y value to 1). I haven't added colliders to the terrain yet so I don't care if hes walking through the crater walls at the moment --- I just don't want him walking UP them.
The logs show that even when the Y value is "clamped" to 1, the character still moves in an upward direction along any terrain height. What am I missing to keep this character grounded on a height of 1 max?
Here is the code:
using UnityEngine;
using System.Collections;
public class ClickCharacterController : MonoBehaviour
{
bool canMove = false;
public int speed = 5;
RaycastHit hit; //Create an object to store all the raycast data
Vector3 groundedHitPoint;
void Start()
{}
void Update()
{
if(Input.GetMouseButton(1))
{
//create a new ray from the source point from the camera to the mouse position
Ray ray = (Camera.main.ScreenPointToRay(Input.mousePosition));
//ray is the origin and vector, and out hit is a vector, passing by reference so it needs out keyword
if (Physics.Raycast(ray, out hit))
{
//Debug.Log("Hit: " + hit.transform.name);
if (hit.transform.name == "Ground")
{
Debug.DrawLine(Camera.main.transform.position, hit.point);
}
//have the cube turn to face the point
transform.LookAt(hit.point);
groundedHitPoint = hit.point;
groundedHitPoint.y = 1;
Debug.Log("groundedhitpoint is : " + groundedHitPoint);
canMove = true;
}
}
if (canMove)
{
//this moves an object
transform.Translate(Vector3.forward*Time.deltaTime*speed);
Debug.Log ("Vector3.forward is: " + Vector3.forward);
//GameObject.FindWithTag("ScavengerSprite").transform.position.y.Equals(1000);
float distance = Vector3.Distance(transform.position, groundedHitPoint);
if (distance <= 0.1F)
{
canMove = false;
}
}
}
}
Thank you!
P.S. I am very new to unity so as simple an answer as possible is much appreciated :)
Answer by Seth-Bergman · May 01, 2012 at 04:24 AM
try moving the "transform.LookAt(hit.point);" line to below where you set groundedHitPoint:
groundedHitPoint = hit.point;
groundedHitPoint.y = 1;
transform.LookAt(groundedHitPoint);
that may work, it would fit the simple criteria
edit:
to be sure, you could go a little further.. Instead of the line:
transform.Translate(Vector3.forward*Time.deltaTime*speed);
use:
transform.position = Vector3.Lerp(transform.position, groundedHitPoint, Time.deltaTime * speed);
Thank you for the fresh eyes and laser solution! That worked. I am using this Lerp function and Im going to have to read up on this it looks like something I should know. Thank you Seth!
Seth, would you know how to add collision to the terrain? I've tried making a bool trigger event (and I have tagged the terrain as Ground):
bool OnCollisionEnter (Collision col)
{
if (col.GameObject("Ground"))
{
return false;
}
else
{
return true;
}
}
The point of this is to turn off movement off when it collides with the terrain.
However I am getting this error "error CS1061: Type UnityEngine.Collision' does not contain a definition for
GameObject' and no extension method GameObject' of type
UnityEngine.Collision' could be found (are you missing a using directive or an assembly reference?)"
Any advice?
Thanks again
if(col.gameObject.tag == "Ground")
also, I don't think adding a return boolean to the OnCollisionEnter is the way to go:
void OnCollisionEnter (Collision col) {
if (col.gameObject.tag == ("Ground"))
{
can$$anonymous$$ove = false;
// or
Stop$$anonymous$$oving(); // if you need more control
}
}
Ahh...I've given it my best shot, here is the whole code. Currently getting a null reference error
NullReferenceException: Object reference not set to an instance of an object ClickCharacterController.OnCollisionEnter (UnityEngine.Collision col, Boolean can$$anonymous$$ove) (at Assets/Scripting/ClickCharacterController.cs:18) ClickCharacterController.Update () (at Assets/Scripting/ClickCharacterController.cs:58)
using UnityEngine;
using System.Collections;
public class ClickCharacterController : $$anonymous$$onoBehaviour
{
bool can$$anonymous$$ove = false;
public int speed = 5;
RaycastHit hit; //Create an object to store all the raycast data
Vector3 groundedHitPoint;
Collision ground;
void Start()
{}
void OnCollisionEnter (Collision col, bool can$$anonymous$$ove)
{
if (col.gameObject.tag == "Ground" )
{
can$$anonymous$$ove = false;
}
else
{
can$$anonymous$$ove = true;
}
}
void Update()
{
if(Input.Get$$anonymous$$ouseButton(1))
{
//create a new ray from the source point from the camera to the mouse position
Ray ray = (Camera.main.ScreenPointToRay(Input.mousePosition));
//ray is the origin and vector, and out hit is a vector, passing by reference so it needs out keyword
if (Physics.Raycast(ray, out hit))
{
//Debug.Log("Hit: " + hit.transform.name);
if (hit.transform.name == "Ground")
{
Debug.DrawLine(Camera.main.transform.position, hit.point);
}
//have the cube turn to face the point
groundedHitPoint = hit.point;
groundedHitPoint.y = 1;
transform.LookAt(groundedHitPoint);
//Debug.Log("groundedhitpoint is : " + groundedHitPoint);
can$$anonymous$$ove = true;
}
}
if (can$$anonymous$$ove)
{
//check for terrain collision
OnCollisionEnter(ground, can$$anonymous$$ove);
//this moves an object
transform.Translate(Vector3.forward*Time.deltaTime*speed);
//Debug.Log ("Vector3.forward is: " + Vector3.forward);
//GameObject.FindWithTag("ScavengerSprite").transform.position.y.Equals(1000);
float distance = Vector3.Distance(transform.position, groundedHitPoint);
if (distance <= 0.1F)
{
can$$anonymous$$ove = false;
}
}
}
}
Answer by Seth-Bergman · May 02, 2012 at 12:33 AM
you don't need to pass "canMove" into the collision function.
void OnCollisionEnter (Collision col)
{
if (col.gameObject.tag == "Ground" )
{
canMove = false;
}
}
the var "canMove" is part of the same script, so it's already accessible.Next:
OnCollisionEnter(ground, canMove);
this line is unnecessary; OnCollisionEnter is automatically entered every time the collider attached to the object hits another collider.
so :
using UnityEngine;
using System.Collections;
public class ClickCharacterController : MonoBehaviour
{
bool canMove = false;
public int speed = 5;
RaycastHit hit; //Create an object to store all the raycast data
Vector3 groundedHitPoint;
void Start()
{}
void OnCollisionEnter ()
{
if (col.gameObject.tag == "Ground" )
{
canMove = false;
}
//else
//{
//canMove = true;
//}
}
void Update()
{
if(Input.GetMouseButton(1))
{
//create a new ray from the source point from the camera to the mouse position
Ray ray = (Camera.main.ScreenPointToRay(Input.mousePosition));
//ray is the origin and vector, and out hit is a vector, passing by reference so it needs out keyword
if (Physics.Raycast(ray, out hit))
{
//Debug.Log("Hit: " + hit.transform.name);
if (hit.transform.name == "Ground")
{
Debug.DrawLine(Camera.main.transform.position, hit.point);
}
//have the cube turn to face the point
groundedHitPoint = hit.point;
groundedHitPoint.y = 1;
transform.LookAt(groundedHitPoint);
//Debug.Log("groundedhitpoint is : " + groundedHitPoint);
canMove = true;
}
}
if (canMove)
{
//this moves an object
transform.Translate(Vector3.forward*Time.deltaTime*speed);
//Debug.Log ("Vector3.forward is: " + Vector3.forward);
//GameObject.FindWithTag("ScavengerSprite").transform.position.y.Equals(1000);
float distance = Vector3.Distance(transform.position, groundedHitPoint);
if (distance <= 0.1F)
{
canMove = false;
}
}
}
Thanks I'll keep hammering away at this till I get it! (Right now its not even registering terrain collision (using a debug.log check). Thanks though your help has definitely put me on the right track!
hmmm.. isTrigger isn't checked, is it (on the player or the terrain)? It shouldn't be for OnCollisionEnter.. also, I think the player shouldn't be kinematic, that might do it too.. Are you using a character controller, or a collider? If it's a collider, rather than a controller, I think you need a rigidbody too.. Just noticed the original post says you haven't added colliders yet, both objects would need one for this to work.
I have an empty game object which has the click to move script. In that I have the herosprite+collider nested in that empty game object I have the script attached to the empty object (which I used to orient the feet at ground level. (though the same happens when I just have the script on just the herosprite+collider).
So the object's child (the hero sprit) has the collider and the empty object has the click to move script. The terrain does not have istrigger checked.
When I add a rigid body it doesn't go through the wall, but it ends up bouncing ridiculously high and all over the place. Sometimes straight up and over. I don't know if theres a particular tutorial or walkthrough you might suggest I look into that explains collisions (the reference manual was not quite helping me). Thanks
does the collision function get entered once you attach the rigidbody?
I think you need the script on the same object as the collider, don't feel like it would work as you describe.. how about attaching the collider to the parent (empty) object?
either way, the alternative is to use a trigger ins$$anonymous$$d, which I believe does not need a rigidbody at any rate, if you don't want one change OnCollisionEnter to OnTriggerEnter and set one of the objects to isTrigger, that should work.. Still need to get the script on the same object though, I'm pretty sure
Im going to have my professor look at this tomorrow. If the answer turns up Ill update you. If not, Ill give a full situation rep :)
Your answer
Follow this Question
Related Questions
Move Camera According to Mouse Movement While Button is Pressed 1 Answer
Move character to specific tiles on map. 1 Answer
Moving an object on the X axis corresponding to mouse movment 0 Answers
Constrain axis of isometric camera ("vertical" only) 0 Answers
Orientation of character controller relative to ground normal? 0 Answers