Coding unit movement - best practices?

For my first practice project, I’m doing RTS/Defense style game.

But before I get any further along, I want to know if I’m going about coding the units in the best manner.
What I have is working so far, but I’m new to Unity, and I’d like some advice on if I’m going about this in a way that won’t cause problems later, or if I’m making things harder than they need to be, etc.

I’m not looking for code optimization at the moment, per se. (though such suggestions are welcome) But rather, if my overall approach is appropriate so far.

Anyway, here’s a brief set up:
I have some vehicles. The vehicles have turrets.
For testing purposes, I can click on a unit to select it. While selected, I can click on another unit and that will set the target for the currently selected unit. It will then chase the unit down while keeping it’s turret aimed at the target.

Relevant components:
The units are rigidbodies so they can crash and stuff.
They have a projector to indicate if they are selected.
Script: Unit_Interaction script (so they player can select and target by clicking)
Script: Unit_Controler (Controls the vehicle movement, and keeps track of waypoints, targets, and general AI)
Script: Turret_Controler (Controls the turret(s) with target objects passed from the Unit_Controler script)

I’ve tried to comment the code as best I can.

Unit_Controler:

#pragma strict

var turnrate : float; //vehicle turn rate
var accell : float; //vehicle accelleration
var topspeed : float; 
var target : GameObject; //current target
var turrettarget : GameObject; //current turret target
var curWPT : Vector3; //current waypoint location (not used yet)

private var rotation : Quaternion;
private var speed : float; //current speed
private var curtopspeed : float; //current top speed (changes based on distance to target for smarter accelleration)
var hasWPT : boolean; //do I have any destination at all?
hasWPT = true;
accell *= .1; //accell is multiplied by .1 for actual value. The larger number are easier to handle when designing the vehicles
var destination : Vector3; //current destination, might be a waypoint, might be a target location

function Update () {
	if(hasWPT == true) { //early out if no destination
		destination = target.transform.position; //destination is currently hardcoded as the current target
		
		//set speed
		var distance = Vector3.Distance(destination, transform.position); //find distance to destination location
		if(distance > 10) { //is destination further than 10 meters away?
			speed += accell; //increase speed by accelleration
			//smart topspeed
			if(distance <= topspeed) { //is the destination distance less than top speed as measured in distance?
				curtopspeed = topspeed * distance/topspeed; //current tops speed will be less than normal topspeed. The closer the destination the lower current top speed will be
			} else { //destination is suffiently far enough away to move at max speed
				curtopspeed = topspeed; //set current top speed to top speed (max)
			}
			//
			if(speed > curtopspeed) {speed = curtopspeed;} //make sure current speed isn't above current top speed. Set to current top speed if neccessary
			if(speed < (curtopspeed*-1) / 2) {speed = (curtopspeed*-1) / 2;} //if moving in reverse, current top speed is half normal (reverse not supported yet)
		} else { //destination is within 10 meters
			//slow down and stop
			speed -= accell * 2; //reduce speed (decelleration is double the accelleration rate)
			if(speed < 0) {speed = 0;} //if speed reduces past 0, set to 0. (moving in reverse not supported yet)
		}
		//turn
		rotation = Quaternion.LookRotation(destination - transform.position); //turning required to face destination
		transform.rotation = Quaternion.RotateTowards(transform.rotation, rotation, turnrate * Time.deltaTime); //do vehicle rotation
		transform.localEulerAngles.x = 0; //keep vehicle level
		transform.localEulerAngles.z = 0;
		transform.Translate(0, 0, speed * Time.deltaTime); //do new vehicle location
		//I have to add some code that detects if a unit's wheeles are touching the ground.
		//If they aren't, then make it so it can't change it's own rotation and position - let physics do it.
	}
}

Turret_Controler:

#pragma strict

var aimspeed : float; //turret rotation speed
var ylimit : float; //turret rotation limit from straight ahead
var xlimit : float; //turret elevation limit

var turret : GameObject; //object that rotates
var gun : GameObject; //object that elevates
private var rotation : Quaternion;
private var elevation : Quaternion;

function Update () {
	var mytarget = GetComponent(Unit_Controler).turrettarget;

	if(mytarget != null) { //rotation required to face target
		rotation = Quaternion.LookRotation(mytarget.transform.position - turret.transform.position);
		elevation = Quaternion.LookRotation(mytarget.transform.position - gun.transform.position);
	} else { //rotation required to return to forward facing
		rotation = Quaternion.LookRotation(transform.position - turret.transform.position);
		elevation = Quaternion.identity;
	}
	//rotate the turret
	turret.transform.rotation = Quaternion.RotateTowards(turret.transform.rotation, rotation, aimspeed * Time.deltaTime);
	turret.transform.localEulerAngles.x = 0; //keep other turret axes level
	turret.transform.localEulerAngles.z = 0;
	//elevate the gun
	gun.transform.rotation = Quaternion.RotateTowards(gun.transform.rotation, elevation, aimspeed * Time.deltaTime);
	gun.transform.localEulerAngles.y = 0; //keep other gun axes level
	gun.transform.localEulerAngles.z = 0;

	if(ylimit != 0) { //0 = no rotation constraint limit
		if(turret.transform.localEulerAngles.y > ylimit  turret.transform.localEulerAngles.y <= 180) { //check if rotation is above limit and targert is on that side of me
			turret.transform.localEulerAngles.y = ylimit; //set to limit
		} else if(turret.transform.localEulerAngles.y < 360-ylimit  turret.transform.localEulerAngles.y > 180) { //check if below, etc.
			turret.transform.localEulerAngles.y = 360-ylimit; //set to limit
		}
	}
	//hardcode depression limit at 4 deg down, for now
	//positive numbers are down, negative numbers are up
	if(gun.transform.localEulerAngles.x > 4  gun.transform.localEulerAngles.x <= 180) { //check if elevation is past limit, etc
		gun.transform.localEulerAngles.x = 4; //set to limit
	}
	if(xlimit != 0) { //0 = no limit
		if(gun.transform.localEulerAngles.x < 360-xlimit  gun.transform.localEulerAngles.x > 180) { //check elevation limit, etc
			gun.transform.localEulerAngles.x = 360-xlimit; //set to limit
		}
	}
}

If your vehicle has a rigidbody component I would move them with rigidbody.AddForce instead of transform.Translate. Also, again if you’re using a rigidbody, handle all things related to physics (movement, rotation, forces) in FixedUpdate. As for the first line in the turret update function, you are constantly getting your Unit_Controler component every frame, rather than just doing:

var myUnitControler : Unit_Controler;

function Start() {
 myUnitControler = GetComponent(Unit_Controler);
}

function Update() {
 myUnitControler.turrettarget;
}

You’ll save some processing power this way! Good luck :slight_smile:

Also a note on some “best practices”. Polymorphism and inheritance provide a great way of creating reusable, clean, efficient code for AI.

biggest thing i can think of is condense that code down to one single unit manager that itterates through your units list and moves them each fixedUpdate

Thanks for the input!

Oh really? Putting the scripts on the objects is bad?
If I just have a central unit manager, I’m going to end up doing a lot of Find() calls every fixedUpdate to operate the individual parts and units. I was under the impression that Find() calls are relatively slow.
Unless you’re saying that same scrips on each unit is worse than the added Find() calls every frame.

I guess I can get around that with an array that holds the object references. But then I have to do a lot of fiddling with that array as units spawn and get killed.

I definitely wouldn’t do that. Let the units manage themselves.

No, putting a script that controls a certain object on that particular object is good design. Messing up this good design just to save a few function calls is among the worst possible premature optimization you can do.

On a separate note, upon reflection, I might not need RigidBodies for this game. I want units to be able to follow terrain contours, but accurate collisions, weapon recoil, and impact forces are just a bonus. The main thing is the terrain is followed and that units don’t pass through each other.

So, will RigidBody physics be overkill in the sense that it will be much more efficient to write code to follow terrain?
I don’t know how much processing overhead the more accurate physics will add.

I still need to learn the RigidBody physics for other projects, though.

I do it by Raycasting the terrain underneath each unit and adjusting based on the hit point.

Ok, but how do you find the angle of terrain at that particular point?

Also, looking at the docs, it looks like colliders need rigidBodies to actually collide with objects. How do I make collisions if I remove rigidBodies? By checking to see if a collision exists and the reduce translation to 0 if true?

EDIT: Oh it doesn’t look like and collision triggers are sent upon two static colliders colliding.

Either go with rigidbodies and addForce or do ray casting and use translate/rotate as necessary.
Doing a translate along with a rigidbody will result in crap performance (by comparison) and lots of odd glitches.

Why has nobody suggested using a character controller for the units yet?

CharacterController + SimpleMove, is probably what you’re looking for.

I’m definitely going to have to take a look at that.

Because it can sometimes be faster to use your own.

http://docs.unity3d.com/Documentation/ScriptReference/RaycastHit-normal.html

im no expert but i was under the impression that a single unit manager script was kinda the norm for games with lots of units… its not that hard to manage, just add the unit to a list on creation and remove when its destroyed. and from the (limited) testing that i did, there was a huge diference in having one script controlling all movements instead of each unit having its own movement script. thats not to say each unit doesnt have a script attached, that holds info for that specific unit like waypoints hp etc. You wont need find just getcomponent to access the script for the unit

http://forum.unity3d.com/threads/100041-Performance-problem-with-modifying-transform.position

There’s a few things to consider in regards to that. One - caching your transform will net you performance gains and Two - not using Update will as well. I use a state machine that moves each unit (that is currently in a move state) via a coroutine. This way the engine handles a list of currently running coroutines instead of me managing a list of units via Update. When the unit gets to his destination he switches to an idle state, the move coroutine completes and is cleaned up accordingly. Works great.

LOL @ Unity and Polymorphism + inheritance. Nicely recited parrot fashion though. :slight_smile: