x


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 :)

more ▼

asked May 01 '12 at 04:15 AM

Aggressor gravatar image

Aggressor
20 1 6 7

(comments are locked)
10|3000 characters needed characters left

2 answers: sort voted first

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);
more ▼

answered May 01 '12 at 04:24 AM

Seth Bergman gravatar image

Seth Bergman
7k 10 16 28

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!

May 01 '12 at 01:16 PM Aggressor

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 forGameObject' and no extension method GameObject' of typeUnityEngine.Collision' could be found (are you missing a using directive or an assembly reference?)"

Any advice?

Thanks again

May 01 '12 at 02:49 PM Aggressor

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"))
{

   canMove = false;

    // or 

   StopMoving(); // if you need more control

}

}

May 01 '12 at 03:23 PM Seth Bergman

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 canMove) (at Assets/Scripting/ClickCharacterController.cs:18) ClickCharacterController.Update () (at Assets/Scripting/ClickCharacterController.cs:58)

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;
Collision ground;

void Start()
{}


void OnCollisionEnter (Collision col, bool canMove)
{
    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)
    {
       //check for terrain collision
       OnCollisionEnter(ground, 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;
       }
    }



}

}

May 01 '12 at 05:40 PM Aggressor
(comments are locked)
10|3000 characters needed characters left

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;
       }
    }

}
more ▼

answered May 02 '12 at 12:33 AM

Seth Bergman gravatar image

Seth Bergman
7k 10 16 28

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!

May 02 '12 at 02:11 AM Aggressor

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.

May 02 '12 at 02:24 AM Seth Bergman

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

May 02 '12 at 12:27 PM Aggressor

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 instead, 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

May 02 '12 at 12:43 PM Seth Bergman

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 :)

May 03 '12 at 01:37 AM Aggressor
(comments are locked)
10|3000 characters needed characters left
Your answer
toggle preview:

Up to 2 attachments (including images) can be used with a maximum of 524.3 kB each and 1.0 MB total.

Follow this question

By Email:

Once you sign in you will be able to subscribe for any updates here

By RSS:

Answers

Answers and Comments

Topics:

x1367
x202
x155
x35

asked: May 01 '12 at 04:15 AM

Seen: 1706 times

Last Updated: May 09 '12 at 02:16 AM