Some AI I'm working on

It’s unfinished AI but it’s getting close to being done. I’m making it for an after-school club that is pretty much Unity3D oriented. We have a robotics team in the school and the club thought it would be cool if we would make the robotics tournament this year, into a Unity3D game. The tournament is Rebound Rumble http://www.youtube.com/watch?v=VkoEvWa62w4 Now we aren’t going to put in the balancing on the bridges part because our game really is just going to be collecting balls and shooting them in the hoops; I decided to make AI for the sake of making it more interesting… and for a good learning experience.

The AI graphics aren’t made yet as it’s just capsules with cubes as faces, but the code part works. I’ve just got to touch up the code some more to where it’d fit in the game scenario. The court isn’t on here, as this is just an AI testing area.

Play As Another Robot
In this webplayer, you play as one of the robots. The AI picks up balls on their side, and throws them at you. You can pick up balls yourself by running over them; you can only hold 3 balls at a time. You can shoot the balls by pressing the LEFT SHIFT.
http://dl.dropbox.com/u/53915463/Player%20AI%20test/WebPlayer.html

Play As a Ball:
In this webplayer, you play from the perspective of one of the balls that are being collected and shot by the AI. http://dl.dropbox.com/u/53915463/AI%20Testing%20Webplayer/WebPlayer.html

AI CODE:

using UnityEngine;
using System.Collections;
using System.Collections.Generic;

public class MyBestAI : MonoBehaviour {
	public Vector3 shootSpot;
	public Vector3 hoop;
	public Vector3 shootBallFrom;

	public float maxDistance;
	public int rotationSpeed;
	public float moveSpeed;

	private GameObject closestBall;

	//The different States being used
	public enum State {
		TargetClosestBall,
		CollectClosestBall,
		RotateToOtherCourtSideAndShootBall,
		NoFreeBalls
	}

	public State state;

	//assign variables in awake and make current state be TargetClosestBall
	public void Awake() {
		shootSpot =  GameObject.Find("shootSpot").transform.position;
		hoop =  GameObject.Find("hoop").transform.position;
		state = State.TargetClosestBall;
	}

	/*make it so collisions don't rotate the AI and
	and also start the Finite State Machine*/
	void Start() {
		rigidbody.freezeRotation = true;
		StartCoroutine(FSM());
	}

	/*can somebody please tell me what exactly this is called 
	 and how it works? All I know is that it finds objects with
	 the tag "FreeBall" and assigns the closest one as closestBall*/
    GameObject FindClosestEnemy() {
        GameObject[] gos;
        gos = GameObject.FindGameObjectsWithTag("FreeBall");
        float distance = Mathf.Infinity;
        Vector3 position = transform.position;

        foreach (GameObject go in gos) {
            Vector3 diff = go.transform.position - position;
            float curDistance = diff.sqrMagnitude;

			if (curDistance < distance) {
				closestBall = go;
                distance = curDistance;
            }
        }
        return null;
    }

	IEnumerator FSM() {

	// Execute the current coroutine (state)
    	while (true)
		yield return StartCoroutine(state.ToString());
  	}

	IEnumerator TargetClosestBall() {
    /* This part works as the Enter function
	of the previous post (it's optional) */
		FindClosestEnemy();	
    	yield return null;

    /* Now we enter in the Execute part of a state
    which will be usually inside a while - yield block */

		//check if closestBall is assigned to an object
		if(closestBall != null) {
			bool moveToBall = true;
			bool ballIsGrabbable = false;
			bool closeToBall = false;
			Vector3 targettedBall = closestBall.transform.position;
			targettedBall.y = transform.position.y;
			
			while (moveToBall) {	

				while(!closeToBall) {

					/*check if the tag is still "FreeBall" in the case 
					 of another AI getting the ball before we do*/
					if (closestBall.tag == "FreeBall") {
						targettedBall = closestBall.transform.position;
						targettedBall.y = transform.position.y;

						//start rotating towards the ball 
						transform.rotation = Quaternion.Slerp(transform.rotation, Quaternion.LookRotation(targettedBall - transform.position), rotationSpeed * Time.deltaTime);
						
						/*if the angle difference between our current look rotation and the look rotation of looking right at the object, 
						 is within 10 degrees; start moving forward. But continue rotating to accommodate for a rolling ball*/
						if(Quaternion.Angle(Quaternion.LookRotation(targettedBall - transform.position), transform.rotation) <= 10) {
							transform.position += transform.forward * (moveSpeed * Time.deltaTime);
							transform.rotation = Quaternion.Slerp(transform.rotation, Quaternion.LookRotation(targettedBall - transform.position), rotationSpeed * Time.deltaTime);

							/*check if the distance between us and the ball, is below or equal to maxDistance, 
							 and if the ball is bellow our current y transform (if the ball is touching the ground)*/
							if(Vector3.Distance(targettedBall, transform.position) <= maxDistance  closestBall.transform.position.y <= gameObject.transform.position.y) {
								ballIsGrabbable = true;
								closeToBall = true;
								
								//do one last check if the tag and name is still "FreeBall" to see if another AI has grabbed the ball already
								if(closestBall.tag == "FreeBall"  closestBall.name == "FreeBall") {
									closestBall.transform.parent = gameObject.transform;
									closestBall.name = "CapturedBall " + gameObject.name;
									closestBall.tag = "CapturedBall";

									closestBall.layer = 10;
								}
								
								//if the tag and/or name is other than "FreeBall" (AKA, ball is already grabbed by other AI)
								else {
									moveToBall = false;
									closestBall = null;
									closeToBall = true;
									state = State.TargetClosestBall;
								}
							}
						}
					}
					
					//if the ball does not have the tag of "FreeBall" (from the first check)
					else {
						moveToBall = false;
						closestBall = null;
						closeToBall = true;
						state = State.TargetClosestBall;
					}
				yield return new WaitForFixedUpdate();
				}
			
				//after (!closeToBall) while loop
				if (ballIsGrabbable  closeToBall) {			
					moveToBall = false;
					state = State.CollectClosestBall;
				}					
			yield return new WaitForFixedUpdate();
			}			
		}

		//there are no FreeBalls on the court, make state NoFreeBalls
		else {
			state = State.NoFreeBalls;
		}

    	/* And finally do something before leaving
    	the state (optional too) and starting a new one */
	}

	IEnumerator CollectClosestBall() {
		//Enter
		bool grabbingBall = false;
		yield return null;

    	//Execute
		int grabbingTime = 2;		

		closestBall.rigidbody.isKinematic = true;
		closestBall.rigidbody.useGravity = false;

		//this check seems unnecessary but I like to have it just incase 
		if(closestBall.name == "CapturedBall " + gameObject.name) {
			grabbingBall = true;				

			while(grabbingBall){
				grabbingTime --;

				/*this check seems necessary, in the case of 
				two AI grabbing a ball at the same time*/
				if (closestBall.name == "CapturedBall " + gameObject.name) {					

					if(grabbingTime <= 0){
						grabbingBall = false;
						closestBall.transform.localPosition = shootBallFrom;
						state = State.RotateToOtherCourtSideAndShootBall;
					}	
				}

				//if another AI has grabbed the ball while we were grabbing it
				else {
					grabbingBall = false;
					state = State.TargetClosestBall;
				}	

				//if still grabbing the ball, wait for 1 second
				if(grabbingBall) {
					yield return new WaitForSeconds(1f);
				}
				
				//else just continue on to the next set of code
				else {
					yield return null;
				}
			}
		}						

		//if the ball's name is not "FreeBall"
		else {
			state = State.TargetClosestBall;
		}
	}

	/*I would like to split this into two IEnumerators "Move to shooting spot"  "Shoot Ball" 
	 to easily make some more advanced shooting options without having while loops inside of while loops.
	 I just think it's more clean if you keep functions seperate don't combine IEnumerators*/
	IEnumerator RotateToOtherCourtSideAndShootBall() {
		//Enter
		yield return null;

		//Execute
		bool canShoot = false;
		bool aiming = false;
		bool rotating = true;	

		//while not in the shooting spot (shootSpot)
		while(!canShoot){
			shootSpot.y = transform.position.y;

			//first rotate towards shootSpot, then move towards it
			if(rotating) {
				transform.rotation = Quaternion.Slerp(transform.rotation, Quaternion.LookRotation(shootSpot - transform.position), rotationSpeed * Time.deltaTime);

					if(Quaternion.Angle(Quaternion.LookRotation(shootSpot - transform.position), transform.rotation) < 1) {
						transform.rotation = Quaternion.LookRotation(shootSpot - transform.position);
						rotating = false;
					}
			}

			//moving towards shootSpot after rotating towards it
			else {

				//while distance from shootSpot is over maxDistance
				if(Vector3.Distance(shootSpot, transform.position) > maxDistance) {
					transform.position += transform.forward * (moveSpeed * Time.deltaTime);
					transform.rotation = Quaternion.Slerp(transform.rotation, Quaternion.LookRotation(shootSpot - transform.position), rotationSpeed * Time.deltaTime);
				}

				//if distance from shootSpot is under/equal to maxDistance
				else {
					canShoot = true;
				}
			}
			yield return new WaitForFixedUpdate();
		}

		//if in the shootSpot
		if (canShoot) {
			aiming = true;

			//rotate towards the player
			while(aiming){
				hoop =  GameObject.Find("Player").transform.position;
				hoop.y = transform.position.y;
				transform.rotation = Quaternion.Slerp(transform.rotation, Quaternion.LookRotation(hoop - transform.position), rotationSpeed * Time.deltaTime);

				if (Quaternion.Angle(Quaternion.LookRotation(hoop - transform.position), transform.rotation) <= 2){
					aiming = false;
				}
				yield return new WaitForFixedUpdate();
			}

			//shoot the ball at the player
			closestBall.transform.parent = null;
			closestBall.rigidbody.useGravity = true;
			closestBall.rigidbody.isKinematic = false;
			closestBall.transform.renderer.material.color = Color.red;
			closestBall.transform.rotation = gameObject.transform.rotation;
			closestBall.rigidbody.AddRelativeForce(0,10,5, ForceMode.Impulse);
			closestBall = null;
			canShoot = false;
		}
		

		//returning back to square one
    	state = State.TargetClosestBall;
	}

	//this just checks if there is a FreeBall, every second
	IEnumerator NoFreeBalls() {
    	   //Enter
    	   yield return null;

		while(closestBall == null) {
			FindClosestEnemy();
		yield return new WaitForSeconds(1);
		}

		//Exit
		state = State.TargetClosestBall;
	}
}

Reading comprehension 101. Read the full post before clicking on the webplayer. I had NO idea what was going on. I just kept flying from place to place and unable to move.

Pretty cool. I’d be curious to watch it from a camera that is stationary, closer to what sports games are broadcast in to help get a better idea of what the action looks like.

i’ll have that happen next webplayer. I thought it’d be a bit more fun this way, being one of the balls. You can still run around if you’re not being grabbed.

And then I’ll add in the actual first person controller you play as.

Stoopid capsules,…

get off me!

Why they throw me?! What’d I do?

:stuck_out_tongue:

nice job.

I’d be interested in hearing more about what checks and flags you are using for the AI. Like what’s in their mind? Closest ball? boundary line,…etc,…

The capsules are prejudice and go after every ball they see… they grew up in the bad part of town. Just be sure to have a fellow ball be in between you and the capsule, and you won’t get thrown.

Then I shall add an explanation of the AI soon, but add it to the top post so it’s easily viewable to all.

Well I’ll be, the Unity Webplayer works with a gyrometer on my EP121 slate.

It might be OK but it moves much too fast. You start the game and it’s over before you can survey anything…

I will slow it down in the next webplayer. Sorry, I had it fast for quick debugging purposes; forgot to make it slow again for actual viewing.

okay I added a second webplayer. In the second one you play as another robot, it’s explained in the top post. This one has the court areas defined. And I shall finish the AI explanation.

everything is moving, rotating, rolling too fast…

in the webplayer where you play as the ball, it’s supposed to do that. The webplayer where you play as the robot, it’s slowed down.

Instead of explaining the AI in a bunch of paragraphs, I’ll just post the code with comments on it. That’ll just help people understand AI better if they want to; it’s some simple AI anyways that I’m not too worried about getting snarfed anyways.

AI code has been posted. Been sick lately, sorry for the delay.