okay, here’s a first version of the code, it’s features are:
-squad movement
-finding cover
-attacking
-following waypoints
-etc…
These parts aren’t working correctly:
-finding cover(
(the characters simply stay stuck in the walls sometimes…)
-following waypoints
(I don’t know why this isn’t working)
Squad1.js
var speed = 3.6;
var runSp = 2.0;
var strafe = 2.0;
private var actStr = 0.0;
// actSp = actual speed...
var actSp = 3.6;
var rotDamp = 6.0;
var rotMod = 6.0;
var hitPoints = 100.0;
var armor = 35.0;
var minDmg = 0.1;
var deadReplacement : Transform;
// Everything for squad movement...
var precision = 0.4;
// atkPrec = precision when attacked...
var atkPrec = 3.0;
var lead : boolean = false;
private var leadingNow : boolean = false;
private var takeLead : boolean = false;
private var leadSwitch = Random.Range(1.5,3.5);
private var ldrLostT = 1.0;
var leader : Squad1;
var leaders : Squad1[];
var maxLdrDist = 25.0;
var followPos : Transform[];
var followBool : Transform[];
var fullSquad : boolean = false;
private var num : int = 0;
var pos : Transform;
// Oos = Out Of Sight...
var Oos : boolean = false;
// atkd = attacked...
var atkd : boolean = false;
var cover : Transform;
var cvrPos : Vector3;
var covered : boolean = false;
var cvrLayer : LayerMask;
var cvrDist = 3.0;
var maxCvrDist = 7.0;
// wayPts = waypoints...
var wayPts : Transform[];
var wayPtTag : String;
var onWay : boolean = false;
var ptObj : Transform;
var curPt : int = 0;
// Attacking and enemy interaction...
var target : Transform;
var targetTag : String;
var attacking : boolean = false;
var strafeFire : boolean = false;
var combatLayers : LayerMask;
var atkRange = 25.0;
var fireRange = 15.0;
var eDist = 0.0;
var upperBody : Transform;
var curWeapon : Transform;
// Some animation options...
var anim : Animation;
var useFireAnim : boolean = true;
var normLookDir : Transform;
// Some speaking...
var hitSound : AudioClip[];
var dieSound : AudioClip[];
var cvrSound : AudioClip;
var fireOrder : AudioClip;
var followOrder : AudioClip;
var leaderLost : AudioClip;
private var lastS = 1.0;
// Some variables for the npc's character...
private var assurance = 1.3;
private var power = 2.0;
private var courage = 1.3;
private var defensive = 0.9;
private var curEmo = 1.0;
function Start () {
if (GetComponent(PersonalityGenerator1)) {
var persona = GetComponent(PersonalityGenerator1);
assurance = persona.assurance;
power = persona.power;
courage = persona.courage;
defensive = persona.defensive;
curEmo = persona.emo;
}
ClosestLeader();
ClosestEnemy();
ClosestWayPt();
if (anim == null) {
anim = GetComponent(Animation);
}
if (anim.animation["shoot0"]) {
anim.animation.AddClip(animation["shoot0"].clip, "shootUpperBody");
anim.animation["shootUpperBody"].AddMixingTransform(upperBody);
anim.animation["strafeL"].layer = -1;
anim.animation["coverWalk"].wrapMode = WrapMode.Loop;
}
}
function FixedUpdate () {
if (lead == false leader == null) {
ClosestLeader();
if (leadingNow == true) {
lead = true;
}
}
// var ldrs = FindObjectsOfType(Squad1);
// for (var i : int; i<leaders.length; i++) {
// if (ldrs[i] != null ldrs[i].lead == true) {
// leaders[i] = ldrs[i];
// }
// }
// for (var ldr : Squad1 in leaders) {
// if (ldr != null ldr != GetComponent(Squad1) ldr.lead == true ldr.fullSquad == false Vector3.Distance(transform.position,ldr.transform.position) < maxLdrDist) {
// leader = ldr;
// }
// }
// FindLeader();
// if (Time.time > lastS + leaderLost.length*2) {
// audio.PlayOneShot(leaderLost);
// }
// }
if (lead == true) {
//check if our squad arrived at its maximum size
for (var posi : Transform in followBool) {
if (posi != null) {
fullSquad = true;
}
else {
fullSquad = false;
}
}
}
if (lead == false leader != null pos == null) {
// Search for a free follow point if the squad isn't full yet
if (num < leader.followPos.length leader.fullSquad == false) {
if (leader.followBool[num] == null) {
Debug.Log(transform.name + ": Squadleader found!");
pos = leader.followPos[num];
leader.followBool[num] = leader.followPos[num];
num = 0;
}
if (leader.followBool[num] != null) {
Debug.Log(transform.name + ": Still searching...");
num += 1;
}
ClosestLeader();
}
else {
//Check for a new leader
leader = null;
ClosestLeader();
pos = null;
num = 0;
}
}
//var hit : RaycastHit;
//if (Physics.Linecast(transform.position, pos.position, hit)) {
// if (hit.transform != null) {
// Oos = true;
// }
// else {
// Oos = false;
// }
//}
if (Oos == true) {
lead = true;
Debug.Log(transform.name + ": Out of my sight...");
}
//Rotation variables...
var rotation : Quaternion;
transform.rotation.x = 0.0;
transform.rotation.z = 0.0;
//check for next enemy
if (target == null) {
ClosestEnemy();
attacking = false;
}
else {
//check if we can see straight onto the target
var ehit : RaycastHit;
if (Physics.Linecast(transform.position,target.position,ehit,combatLayers)) {
if (ehit.transform == target || ehit.transform.parent == target) {
//if enemy is in range, switch to attack mode...
if (ehit.distance < atkRange) {
attacking = true;
atkd = true;
}
else {
atkd = true;
}
}
//if not, cancel attack mode...
else {
attacking = false;
}
}
}
// If we are attacking the enemy:
if (attacking == true) {
//Check the distance between us and the enemy
eDist = Vector3.Distance(transform.position,target.position);
//Rotate towards it:
if (target != null) {
rotation = Quaternion.LookRotation(target.position - transform.position);
}
//Or towards the follow point if enemy is null...
else {
rotation = Quaternion.LookRotation(pos.position - transform.position);
}
//Then do the rotation:
transform.rotation = Quaternion.Slerp(transform.rotation, rotation, Time.deltaTime * rotDamp);
// if the enemy comes into attacking range...
if (eDist < atkRange eDist > fireRange) {
//... start aiming at him, ...
if (anim.GetComponent("HeadLookController")) {
anim.GetComponent("HeadLookController").target = target.position;
}
//... give a fire order, ...
if (Time.time > lastS + Random.Range(fireOrder.length*3,10.0)) {
audio.PlayOneShot(fireOrder);
lastS = Time.time;
}
//... and go closer to the enemy to shoot him.
if (eDist > fireRange+0.1) {
anim.animation["coverWalk"].wrapMode = WrapMode.Loop;
anim.animation.CrossFade("coverWalk", 0.05);
rigidbody.velocity = (transform.forward * actSp/runSp*1.7)+(transform.right * actStr * strafe)+(transform.up*rigidbody.velocity.y);
}
else {
rigidbody.velocity = (transform.forward * actSp/runSp*1.7)+(transform.up*rigidbody.velocity.y)+(transform.right*rigidbody.velocity.x);
}
}
// If enemy is closer than firing range, ...
if (eDist < fireRange) {
// ... keep aiming on him, ...
if (anim.GetComponent("HeadLookController")) {
anim.GetComponent("HeadLookController") .target = target.position;
}
// ... and open fire!
if (curWeapon) {
curWeapon.SendMessage("Fire", SendMessageOptions.DontRequireReceiver);
}
// If enabled, walk around the target and keep shooting ...
if (strafeFire == true) {
rigidbody.velocity = (transform.forward * actSp/rotMod)+(transform.right * -1 * strafe)+(transform.up*rigidbody.velocity.y);
anim.animation["strafeL"].wrapMode = WrapMode.Loop;
anim.animation.CrossFade("strafeL", 0.2);
if (useFireAnim == true) {
animation.CrossFadeQueued("shootUpperBody", 0.3, QueueMode.PlayNow);
}
}
// ... or just stand there and blow him to hell!
else {
if (useFireAnim == true) {
// Play a fire animation:
anim.animation["shoot1"].wrapMode = WrapMode.Loop;
anim.animation.CrossFade("shoot1", 0.2);
}
else {
// Play animation depending on if we are standing or shooting from cover.
if (covered == true) {
anim.animation.CrossFade("coverIdle", 0.05);
}
else {
anim.animation.CrossFade("idle", 0.05);
}
}
}
}
}
// If we aren't attacking the target:
else {
// Aim straight in front of you:
if (anim.GetComponent("HeadLookController")) {
anim.GetComponent("HeadLookController").target = normLookDir.position;
}
if (covered == false) {
if (leader != null pos != null) {
// rotate towards the point we are following:
rotation = Quaternion.LookRotation(pos.position - transform.position);
rotation.x = 0.0;
rotation.z = 0.0;
transform.rotation = Quaternion.Slerp(transform.rotation, rotation, Time.deltaTime * rotDamp);
}
else {
// or look for a new leader if we lost the old one:
ClosestLeader();
}
}
// if we are attacked and currently covered, but when we ca't see an enemy:
if (atkd == true covered == true) {
// rotate towards a cover or place to hide behind:
if (cover != null) {
rotation = Quaternion.LookRotation(cvrPos - transform.position);
rotation.x = 0.0;
rotation.z = 0.0;
transform.rotation = Quaternion.Slerp(transform.rotation, rotation, Time.deltaTime * rotDamp * defensive);
}
// If there's no such place, ...
else {
// ... keep following the squad leader.
rotation = Quaternion.LookRotation(pos.position - transform.position);
rotation.x = 0.0;
rotation.z = 0.0;
transform.rotation = Quaternion.Slerp(transform.rotation, rotation, Time.deltaTime * rotDamp);
}
}
// If we are not the squad leader:
if (pos != null lead == false) {
// Find the follow point's position:
var posRel = Vector3(pos.position.x,transform.position.y,pos.position.z);
// If we are not being attacked:
if (atkd == false) {
// If the point is to far away:
if (Vector3.Distance(transform.position, posRel) > precision) {
// ... play a movement animation, ...
if (anim.animation["runSlow"]) {
anim.animation["runSlow"].wrapMode = WrapMode.Loop;
anim.animation.CrossFade("runSlow", 0.1);
}
else {
anim.animation.CrossFade("coverWalk", 0.15);
}
// ... and simply move, until we are close enough.
rigidbody.velocity = (transform.forward * actSp)+(transform.right * actStr * strafe)+(transform.up*rigidbody.velocity.y);
}
// If we are close enough, ...
else {
// ... let's just stand around.
anim.animation.CrossFade("idle", 0.2);
}
// ... and tell the user who we are following:
Debug.Log(transform.name + ": Now following " + pos.name + " of " + leader.transform.name);
}
// But if we are being attaked:
else {
// and if we are covered, ...
if (covered == true) {
// ... If the distance to our leader is longer than it should, ...
if (Vector3.Distance(transform.position, leader.transform.position) > atkPrec * assurance * courage) {
if (Vector3.Distance(transform.position, cvrPos) > cvrDist*10) {
// ... then just come closer to the leader.
if (anim.animation["coverWalk"]) {
anim.animation["coverWalk"].wrapMode = WrapMode.Loop;
anim.animation.CrossFade("coverWalk", 0.1);
}
else {
anim.animation["walk"].wrapMode = WrapMode.Loop;
anim.animation.CrossFade("walk", 0.1);
}
rigidbody.velocity = (transform.forward * actSp/runSp*1.7)+(transform.right * actStr * strafe)+(transform.up*rigidbody.velocity.y);
}
else {
// or just keep sitting around.
if (anim.animation["coverIdle"]) {
anim.animation.CrossFade("coverIdle", 0.2);
}
else {
anim.animation.CrossFade("idle", 0.2);
}
}
}
// But if the distance to the leader is short:
else {
// ... just stand around.
covered = false;
cover = null;
anim.animation.CrossFade("idle",0.3);
}
// But now let's search for possible covers:
var cvrs = GameObject.FindGameObjectsWithTag("cover");
for (var cvr : GameObject in cvrs) {
var cvF : RaycastHit;
if (Physics.Linecast(transform.position, cvr.transform.position, cvF, cvrLayer)) {
// ... and select a very close one.
if (cvF.distance < (maxCvrDist*defensive*assurance/courage)) {
cover = cvF.transform;
cvrPos = cvF.point;
}
}
}
}
// If we are not hiding but still being attacked:
else {
//... and if we are far away from follow point:
if (Vector3.Distance(transform.position, posRel) > atkPrec) {
//... simply run towards it.
if (anim.animation["runFast"]) {
anim.animation["runFast"].wrapMode = WrapMode.Loop;
anim.animation.CrossFade("runFast", 0.2);
}
else {
anim.animation["coverWalk"].wrapMode = WrapMode.Loop;
anim.animation.CrossFade("coverWalk", 0.2);
}
rigidbody.velocity = (transform.forward * actSp * runSp)+(transform.right * actStr * strafe)+(transform.up*rigidbody.velocity.y);
}
// otherwise just stand around like a stupid...
else {
anim.animation.CrossFade("idle", 0.2);
}
}
}
}
}
// If we are the squad leader:
if (lead == true) {
// and if we aren't being attacked or attacking and if we've got waypoints:
if (atkd == false attacking == false ptObj != null) {
// Aim straight in front of us:
if (anim.GetComponent("HeadLookController")) {
anim.GetComponent("HeadLookController").target = normLookDir.position;
}
// And follow the current waypoint...
onWay = true;
rotation = Quaternion.LookRotation(ptObj.position - transform.position);
rotation.x = 0.0;
rotation.z = 0.0;
transform.rotation = Quaternion.Slerp(transform.rotation, rotation, Time.deltaTime * 3);
var wayPt = ptObj.GetComponent(SquadWayPoint1);
// ... as long as it is at a certain distance:
if (Vector3.Distance(transform.position,ptObj.position) >= precision) {
ImThere = false;
rigidbody.velocity = (transform.forward * actSp)+(transform.up*rigidbody.velocity.y);
anim.animation["coverWalk"].wrapMode = WrapMode.Loop;
anim.animation.CrossFade("coverWalk", 0.05);
}
// otherwise just take the next one or wait:
else {
animation.CrossFade("idle", 0.2);
if (wayPt.holdPos == true) {
if (ImThere == false) {
holdT = Time.time;
ImThere = true;
}
if (Time.time > holdT + wayPt.holdT) {
nextPt = Random.Range(0,wayPt.wayPts.length+1);
if (nextPt < wayPt.wayPts.length) {
ptObj = wayPt.wayPts[nextPt];
}
else {
ptObj = wayPt.wayPts[wayPt.priority];
}
}
}
else {
ptObj = ptObj.GetComponent(SquadWayPoint1).wayPts[ptObj.GetComponent(SquadWayPoint1).priority];
}
}
}
// But if we don't have any waypoint:
if (atkd == false attacking == false ptObj == null) {
// we are not on a way, so...
onWay = false;
// ... let's just stand 'round.
rigidbody.velocity = (Vector3.zero)+(transform.up*rigidbody.velocity.y);
}
}
}
function FindLeader () {
Debug.Log(transform.name + ": Hey, I'm so fuckin' alone, guys!");
if (leader == GetComponent(Squad1)) {
var ldrs = FindObjectsOfType(Squad1);
for (var i : int; i<leaders.length; i++) {
leaders[i] = ldrs[i];
}
for (var ldr : Squad1 in leaders) {
if (ldr != GetComponent(Squad1) ldr.lead == true ldr.fullSquad == false Vector3.Distance(transform.position,ldr.transform.position) < maxLdrDist) {
leader = ldr;
}
}
}
}
function ClosestLeader () { //: GameObject
if (lead == false leader == null) {
// Find all game objects with our tag
var gos : GameObject[];
gos = GameObject.FindGameObjectsWithTag(gameObject.tag);
var closest : GameObject;
var distance = Mathf.Infinity;
var position = transform.position;
// Iterate through them and find the closest one
for (var go : GameObject in gos) {
if (go.GetComponent(Squad1) go.GetComponent(Squad1).lead == true go.GetComponent(Squad1).fullSquad == false) {
var diff = (go.transform.position - position);
var curDistance = diff.sqrMagnitude;
if (curDistance < distance) {
closest = go;
distance = curDistance;
leader = go.GetComponent(Squad1);
}
}
}
//return closest;
}
if (leader == null) {
if (leadingNow == false takeLead == false) {
ldrLostT = Time.time;
takeLead = true;
}
if (leadingNow == false takeLead == true Time.time > ldrLostT + leadSwitch) {
leadingNow = true;
if (anim.animation["wink"]) {
anim.animation.CrossFade("wink", 0.2);
}
}
if (leadingNow == true) {
leader = null;
lead = true;
if (Time.time > lastS + Random.Range(7.0,27.0)) {
if (attacking == false) {
audio.PlayOneShot(followOrder, assurance);
}
else {
audio.PlayOneShot(fireOrder, assurance);
}
lastS = Time.time;
}
}
}
yield new WaitForSeconds(0.5);
}
function ClosestEnemy () { //: GameObject
// Find all game objects with enemy's tag
var gos : GameObject[];
gos = GameObject.FindGameObjectsWithTag(targetTag);
var closest : GameObject;
var distance = Mathf.Infinity;
var position = transform.position;
// Iterate through them and find the closest one
for (var go : GameObject in gos) {
var diff = (go.transform.position - position);
var curDistance = diff.sqrMagnitude;
if (curDistance < distance) {
closest = go;
distance = curDistance;
target = go.transform;
}
}
//return closest;
yield new WaitForSeconds(1);
}
function ClosestWayPt () { //: GameObject
// Find all waypoint gameObjects
var gos : GameObject[];
gos = GameObject.FindGameObjectsWithTag(wayPtTag);
var closest : GameObject;
var distance = Mathf.Infinity;
var position = transform.position;
// Iterate through them and find the closest one
for (var go : GameObject in gos) {
var diff = (go.transform.position - position);
var curDistance = diff.sqrMagnitude;
if (curDistance < distance) {
closest = go;
distance = curDistance;
ptObj = go.transform;
//wayPts = go.GetComponent(SquadWayPoint1).wayPts;
//for (var i : int; i<wayPts.length; i++) {
// if (wayPts[i] == go.transform) {
// curPt = i;
// }
//}
}
}
//return closest;
yield;
}
function ApplyDamage (dmg : float) {
if (dmg >= minDmg) {
if (armor > 0.0) {
if (armor < dmg) {
armor -= dmg;
hitPoints -= (dmg - armor)/1.5;
}
else {
armor -= dmg;
}
}
else {
hitPoints -= dmg;
if (hitPoints <= 0.0) {
Detonate();
}
}
atkd = true;
if (Time.time > lastS + cvrSound.length*4) {
var goCvrd = (Random.Range(0.0,10)*courage)-defensive*2;
if (goCvrd < 5.0) {
cvrd = true;
audio.PlayOneShot(cvrSound);
}
else {
cvrd = false;
audio.PlayOneShot(hitSound[Random.Range(0,hitSound.length)]);
}
lastS = Time.time;
}
}
}
function Detonate () {
Destroy(gameObject);
if (dieSound)
AudioSource.PlayClipAtPoint(dieSound[Random.Range(0,dieSound.length)], transform.position);
// Replace ourselves with the dead body
if (deadReplacement) {
var dead : Transform = Instantiate(deadReplacement, transform.position, transform.rotation);
// Copy position rotation from the old hierarchy into the dead replacement
CopyTransformsRecurse(transform, dead);
}
}
static function CopyTransformsRecurse (src : Transform, dst : Transform) {
dst.position = src.position;
dst.rotation = src.rotation;
for (var child : Transform in dst)
{
// Match the transform with the same name
var curSrc = src.Find(child.name);
if (curSrc)
CopyTransformsRecurse(curSrc, child);
}
}
And the waypoint script:
// The possible directions characters can go:
var wayPts : Transform[];
// The waypoint they should prefer:
// (indicated by wayPts' index numbers)
var priority : int = 1;
// should characters protect this position or wait there?
var holdPos : boolean = false;
// how long should they wait there for enemies?
var holdT = 10.0;