Unity (五)
2015-08-11 18:48
726 查看
前两天一直在看书,看视频,今天总结一下代码哈~~~
官网Roguelike案例。
Loader,用于开始游戏,负责调用GameManager的单例,SoundManager的单例;
GameManager 在这里调用了BoardManager。并且在这里实现Player和Enemy的调用和管理;
BoardManager 地图逻辑;
MovingObject 移动逻辑,包括Player和Enemy的移动;
Player
Enemy
Wall 墙,障碍物,关卡trigger;
SoundManager 音乐和音效的管理;
官网Roguelike案例。
Loader,用于开始游戏,负责调用GameManager的单例,SoundManager的单例;
using UnityEngine; using System.Collections; public class Loader : MonoBehaviour { public GameObject gameManager; //GameManager prefab to instantiate. public GameObject soundManager; //SoundManager prefab to instantiate. void Awake() { //Check if a GameManager has already been assigned to static variable GameManager.instance or if it's still null if (GameManager.instance == null) //Instantiate gameManager prefab Instantiate(gameManager); //Check if a SoundManager has already been assigned to static variable GameManager.instance or if it's still null if (SoundManager.instance == null) //Instantiate SoundManager prefab Instantiate(soundManager); } }
GameManager 在这里调用了BoardManager。并且在这里实现Player和Enemy的调用和管理;
using UnityEngine; using System.Collections; using System.Collections.Generic; //Allows us to use Lists. using UnityEngine.UI; //Allows us to use UI. public class GameManager : MonoBehaviour { public float levelStartDelay = 2f; //Time to wait before starting level, in seconds. public float turnDelay = 0.1f; //Delay between each Player turn. public int playerFoodPoints = 100; //Starting value for Player food points. public static GameManager instance = null; //Static instance of GameManager which allows it to be accessed by any other script. [HideInInspector] public bool playersTurn = true; //Boolean to check if it's players turn, hidden in inspector but public. private Text levelText; //Text to display current level number. private GameObject levelImage; //Image to block out level as levels are being set up, background for levelText. private BoardManager boardScript; //Store a reference to our BoardManager which will set up the level. private int level = 1; //Current level number, expressed in game as "Day 1". private List<Enemy> enemies; //List of all Enemy units, used to issue them move commands. private bool enemiesMoving; //Boolean to check if enemies are moving. private bool doingSetup = true; //Boolean to check if we're setting up board, prevent Player from moving during setup. //Awake is always called before any Start functions void Awake() { //Check if instance already exists if (instance == null) //if not, set instance to this instance = this; //If instance already exists and it's not this: else if (instance != this) //Then destroy this. This enforces our singleton pattern, meaning there can only ever be one instance of a GameManager. Destroy(gameObject); //Sets this to not be destroyed when reloading scene DontDestroyOnLoad(gameObject); //Assign enemies to a new List of Enemy objects. enemies = new List<Enemy>(); //Get a component reference to the attached BoardManager script boardScript = GetComponent<BoardManager>(); //Call the InitGame function to initialize the first level InitGame(); } //This is called each time a scene is loaded. void OnLevelWasLoaded(int index) { //Add one to our level number. level++; //Call InitGame to initialize our level. InitGame(); } //Initializes the game for each level. void InitGame() { //While doingSetup is true the player can't move, prevent player from moving while title card is up. doingSetup = true; //Get a reference to our image LevelImage by finding it by name. levelImage = GameObject.Find("LevelImage"); //Get a reference to our text LevelText's text component by finding it by name and calling GetComponent. levelText = GameObject.Find("LevelText").GetComponent<Text>(); //Set the text of levelText to the string "Day" and append the current level number. levelText.text = "Day " + level; //Set levelImage to active blocking player's view of the game board during setup. levelImage.SetActive(true); //Call the HideLevelImage function with a delay in seconds of levelStartDelay. Invoke("HideLevelImage", levelStartDelay); //Clear any Enemy objects in our List to prepare for next level. enemies.Clear(); //Call the SetupScene function of the BoardManager script, pass it current level number. boardScript.SetupScene(level); } //Hides black image used between levels void HideLevelImage() { //Disable the levelImage gameObject. levelImage.SetActive(false); //Set doingSetup to false allowing player to move again. doingSetup = false; } //Update is called every frame. void Update() { //Check that playersTurn or enemiesMoving or doingSetup are not currently true. if (playersTurn || enemiesMoving || doingSetup) //If any of these are true, return and do not start MoveEnemies. return; //Start moving enemies. StartCoroutine(MoveEnemies()); } //Call this to add the passed in Enemy to the List of Enemy objects. public void AddEnemyToList(Enemy script) { //Add Enemy to List enemies. enemies.Add(script); } //GameOver is called when the player reaches 0 food points public void GameOver() { //Set levelText to display number of levels passed and game over message levelText.text = "After " + level + " days, you starved."; //Enable black background image gameObject. levelImage.SetActive(true); //Disable this GameManager. enabled = false; } //Coroutine to move enemies in sequence. IEnumerator MoveEnemies() { //While enemiesMoving is true player is unable to move. enemiesMoving = true; //Wait for turnDelay seconds, defaults to .1 (100 ms). yield return new WaitForSeconds(turnDelay); //If there are no enemies spawned (IE in first level): if (enemies.Count == 0) { //Wait for turnDelay seconds between moves, replaces delay caused by enemies moving when there are none. yield return new WaitForSeconds(turnDelay); } //Loop through List of Enemy objects. for (int i = 0; i < enemies.Count; i++) { //Call the MoveEnemy function of Enemy at index i in the enemies List. enemies[i].MoveEnemy(); //Wait for Enemy's moveTime before moving next Enemy, yield return new WaitForSeconds(enemies[i].moveTime); } //Once Enemies are done moving, set playersTurn to true so player can move. playersTurn = true; //Enemies are done moving, set enemiesMoving to false. enemiesMoving = false; } }
BoardManager 地图逻辑;
using UnityEngine; using System; using System.Collections.Generic; //Allows us to use Lists.允许我们使用列表; using Random = UnityEngine.Random; //Tells Random to use the Unity Engine random number generator.告诉编译器Random要用UnityEngine的Random; [System.Serializable] //Using Serializable allows us to embed a class with sub properties in the inspector.使得变量可以被Inspector界面获取到; //namespace Completed //{ public class BoardManager : MonoBehaviour { public class Count { public int minimum; public int maximum; public Count(int min, int max) //构造函数; { minimum = min; maximum = max; } } public int columns = 8; public int rows = 8; public Count wallCount = new Count(5,9); //wall的随机范围; public Count foodCount = new Count(1,5); //food的随机范围; public GameObject exit; //Prefab to spawn for exit. public GameObject[] floorTiles; //Array of floor prefabs. public GameObject[] wallTiles; //Array of wall prefabs. public GameObject[] foodTiles; //Array of food prefabs. public GameObject[] enemyTiles; //Array of enemy prefabs. public GameObject[] outerWallTiles; //Array of outer tile prefabs. private Transform boardHolder; //A variable to store a reference to the transform of our Board object.用于备份当前BoardObject的transform 的标记; private List <Vector3> gridPositions = new List <Vector3> (); //A list of possible locations to place tiles.地图瓦片的坐标列表; //Clears our list gridPositions and prepares it to generate a new board. void InitialiseList() { //Clear our list gridPositions. gridPositions.Clear(); //Loop through x axis (columns). for (int x = 1; x < columns - 1; x++) { //Within each column, loop through y axis (rows). for (int y = 1; y < rows - 1; y++) { //At each index add a new Vector3 to our list with the x and y coordinates of that position. gridPositions.Add(new Vector3(x, y, 0f)); } } } //Sets up the outer walls and floor (background) of the game board. void BoardSetup() { //Instantiate Board and set boardHolder to its transform. boardHolder = new GameObject("Board").transform; //Loop along x axis, starting from -1 (to fill corner) with floor or outerwall edge tiles. for (int x = -1; x < columns + 1; x++) { //Loop along y axis, starting from -1 to place floor or outerwall tiles. for (int y = -1; y < rows + 1; y++) { //Choose a random tile from our array of floor tile prefabs and prepare to instantiate it. GameObject toInstantiate = floorTiles[Random.Range(0, floorTiles.Length)]; //Check if we current position is at board edge, if so choose a random outer wall prefab from our array of outer wall tiles. if (x == -1 || x == columns || y == -1 || y == rows) toInstantiate = outerWallTiles[Random.Range(0, outerWallTiles.Length)]; //Instantiate the GameObject instance using the prefab chosen for toInstantiate at the Vector3 corresponding to current grid position in loop, cast it to GameObject. GameObject instance = Instantiate(toInstantiate, new Vector3(x, y, 0f), Quaternion.identity) as GameObject; //Set the parent of our newly instantiated object instance to boardHolder, this is just organizational to avoid cluttering hierarchy.使所有新建测实例都在一个父级下; instance.transform.SetParent(boardHolder); } } } //RandomPosition returns a random position from our list gridPositions. Vector3 RandomPosition() { //Declare an integer randomIndex, set it's value to a random number between 0 and the count of items in our List gridPositions. int randomIndex = Random.Range(0, gridPositions.Count); //Declare a variable of type Vector3 called randomPosition, set it's value to the entry at randomIndex from our List gridPositions. Vector3 randomPosition = gridPositions[randomIndex]; //Remove the entry at randomIndex from the list so that it can't be re-used.用一个,删一个,防止再次使用; gridPositions.RemoveAt(randomIndex); //Return the randomly selected Vector3 position. return randomPosition; } //LayoutObjectAtRandom accepts an array of game objects to choose from along with a minimum and maximum range for the number of objects to create. void LayoutObjectAtRandom(GameObject[] tileArray, int minimum, int maximum) { //Choose a random number of objects to instantiate within the minimum and maximum limits int objectCount = Random.Range(minimum, maximum + 1); //Instantiate objects until the randomly chosen limit objectCount is reached for (int i = 0; i < objectCount; i++) { //Choose a position for randomPosition by getting a random position from our list of available Vector3s stored in gridPosition Vector3 randomPosition = RandomPosition(); //Choose a random tile from tileArray and assign it to tileChoice GameObject tileChoice = tileArray[Random.Range(0, tileArray.Length)]; //Instantiate tileChoice at the position returned by RandomPosition with no change in rotation Instantiate(tileChoice, randomPosition, Quaternion.identity); } } //SetupScene initializes our level and calls the previous functions to lay out the game board public void SetupScene(int level) { //Creates the outer walls and floor. BoardSetup(); //Reset our list of gridpositions. InitialiseList(); //Instantiate a random number of wall tiles based on minimum and maximum, at randomized positions. LayoutObjectAtRandom(wallTiles, wallCount.minimum, wallCount.maximum); //Instantiate a random number of food tiles based on minimum and maximum, at randomized positions. LayoutObjectAtRandom(foodTiles, foodCount.minimum, foodCount.maximum); //Determine number of enemies based on current level number, based on a logarithmic progression int enemyCount = (int)Mathf.Log(level, 2f); //Instantiate a random number of enemies based on minimum and maximum, at randomized positions. LayoutObjectAtRandom(enemyTiles, enemyCount, enemyCount); //Instantiate the exit tile in the upper right hand corner of our game board Instantiate(exit, new Vector3(columns - 1, rows - 1, 0f), Quaternion.identity); } }
MovingObject 移动逻辑,包括Player和Enemy的移动;
using UnityEngine; using System.Collections; //The abstract keyword enables you to create classes and class members that are incomplete and must be implemented in a derived class. public abstract class MovingObject : MonoBehaviour { public float moveTime = 0.1f; //Time it will take object to move, in seconds. public LayerMask blockingLayer; //Layer on which collision will be checked.//碰撞检测层; private BoxCollider2D boxCollider; //The BoxCollider2D component attached to this object. private Rigidbody2D rb2D; //The Rigidbody2D component attached to this object. private float inverseMoveTime; //Used to make movement more efficient.使运动更高效; //Protected, virtual functions can be overridden by inheriting classes. protected virtual void Start() { //Get a component reference to this object's BoxCollider2D boxCollider = GetComponent<BoxCollider2D>(); //Get a component reference to this object's Rigidbody2D rb2D = GetComponent<Rigidbody2D>(); //By storing the reciprocal of the move time we can use it by multiplying instead of dividing, this is more efficient. inverseMoveTime = 1f / moveTime; } //Move returns true if it is able to move and false if not. //Move takes parameters for x direction, y direction and a RaycastHit2D to check collision. protected bool Move(int xDir, int yDir, out RaycastHit2D hit) { //Store start position to move from, based on objects current transform position. Vector2 start = transform.position; //隐式转换; // Calculate end position based on the direction parameters passed in when calling Move. Vector2 end = start + new Vector2(xDir, yDir); //Disable the boxCollider so that linecast doesn't hit this object's own collider. boxCollider.enabled = false; //Cast a line from start point to end point checking collision on blockingLayer. hit = Physics2D.Linecast(start, end, blockingLayer); //是判断一条直线的碰撞检测吗?; //Re-enable boxCollider after linecast boxCollider.enabled = true; //Check if anything was hit if (hit.transform == null) { //If nothing was hit, start SmoothMovement co-routine passing in the Vector2 end as destination StartCoroutine(SmoothMovement(end)); //Return true to say that Move was successful return true; } //If something was hit, return false, Move was unsuccesful. return false; } //Co-routine for moving units from one space to next, takes a parameter end to specify where to move to. protected IEnumerator SmoothMovement(Vector3 end) { //Calculate the remaining distance to move based on the square magnitude of the difference between current position and end parameter. //Square magnitude is used instead of magnitude because it's computationally cheaper. float sqrRemainingDistance = (transform.position - end).sqrMagnitude;//sqrMagnitude效率更高; //向量的长度是用勾股定理计算出来,计算机计算两次方和开根的运算量比加减法要费时的多。所以如果是想比较两个向量的长度,用sqrMagnitude可以快出很多; //While that distance is greater than a very small amount (Epsilon, almost zero): while (sqrRemainingDistance > float.Epsilon) //float.Epsilon无限趋于零的正浮点数; { //Find a new position proportionally closer to the end, based on the moveTime Vector3 newPostion = Vector3.MoveTowards(rb2D.position, end, inverseMoveTime * Time.deltaTime);//MoveTowards是在一条直线上移动一个点; //Call MovePosition on attached Rigidbody2D and move it to the calculated position. rb2D.MovePosition(newPostion); //Recalculate the remaining distance after moving. sqrRemainingDistance = (transform.position - end).sqrMagnitude; //Return and loop until sqrRemainingDistance is close enough to zero to end the function yield return null; } } //The virtual keyword means AttemptMove can be overridden by inheriting classes using the override keyword. //AttemptMove takes a generic parameter T to specify the type of component we expect our unit to interact with if blocked (Player for Enemies, Wall for Player). protected virtual void AttemptMove<T>(int xDir, int yDir) where T : Component { //Hit will store whatever our linecast hits when Move is called. RaycastHit2D hit; //Set canMove to true if Move was successful, false if failed. bool canMove = Move(xDir, yDir, out hit); //Check if nothing was hit by linecast if (hit.transform == null) //If nothing was hit, return and don't execute further code. return; //Get a component reference to the component of type T attached to the object that was hit T hitComponent = hit.transform.GetComponent<T>(); //If canMove is false and hitComponent is not equal to null, meaning MovingObject is blocked and has hit something it can interact with. if (!canMove && hitComponent != null) //Call the OnCantMove function and pass it hitComponent as a parameter. OnCantMove(hitComponent); } //The abstract modifier indicates that the thing being modified has a missing or incomplete implementation. //OnCantMove will be overriden by functions in the inheriting classes. protected abstract void OnCantMove<T>(T component) where T : Component; }
Player
using UnityEngine; using System.Collections; using UnityEngine.UI; //Allows us to use UI. //Player inherits from MovingObject, our base class for objects that can move, Enemy also inherits from this. public class Player : MovingObject { public float restartLevelDelay = 1f; //Delay time in seconds to restart level. public int pointsPerFood = 10; //Number of points to add to player food points when picking up a food object. public int pointsPerSoda = 20; //Number of points to add to player food points when picking up a soda object. public int wallDamage = 1; //How much damage a player does to a wall when chopping it. public Text foodText; //UI Text to display current player food total. public AudioClip moveSound1; //1 of 2 Audio clips to play when player moves. public AudioClip moveSound2; //2 of 2 Audio clips to play when player moves. public AudioClip eatSound1; //1 of 2 Audio clips to play when player collects a food object. public AudioClip eatSound2; //2 of 2 Audio clips to play when player collects a food object. public AudioClip drinkSound1; //1 of 2 Audio clips to play when player collects a soda object. public AudioClip drinkSound2; //2 of 2 Audio clips to play when player collects a soda object. public AudioClip gameOverSound; //Audio clip to play when player dies. private Animator animator; //Used to store a reference to the Player's animator component.//存储Player动画的编号; private int food; //Used to store player food points total during level. private Vector2 touchOrigin = -Vector2.one; //Used to store location of screen touch origin for mobile controls. //Start overrides the Start function of MovingObject protected override void Start() { //Get a component reference to the Player's animator component animator = GetComponent<Animator>(); //Get the current food point total stored in GameManager.instance between levels. food = GameManager.instance.playerFoodPoints; //Set the foodText to reflect the current player food total. foodText.text = "Food: " + food; //Call the Start function of the MovingObject base class. base.Start(); } //This function is called when the behaviour becomes disabled or inactive.当behavior禁用或无效时调用函数; private void OnDisable() { //When Player object is disabled, store the current local food total in the GameManager so it can be re-loaded in next level. GameManager.instance.playerFoodPoints = food; } private void Update() { //If it's not the player's turn, exit the function. if (!GameManager.instance.playersTurn) return; int horizontal = 0; //Used to store the horizontal move direction. int vertical = 0; //Used to store the vertical move direction. //Check if we are running either in the Unity editor or in a standalone build. #if UNITY_STANDALONE || UNITY_WEBPLAYER //Get input from the input manager, round it to an integer and store in horizontal to set x axis move direction horizontal = (int)(Input.GetAxisRaw("Horizontal")); //Get input from the input manager, round it to an integer and store in vertical to set y axis move direction vertical = (int)(Input.GetAxisRaw("Vertical")); //Check if moving horizontally, if so set vertical to zero. if (horizontal != 0) { vertical = 0; } //Check if we are running on iOS, Android, Windows Phone 8 or Unity iPhone #elif UNITY_IOS || UNITY_ANDROID || UNITY_WP8 || UNITY_IPHONE //Check if Input has registered more than zero touches if (Input.touchCount > 0) { //Store the first touch detected. Touch myTouch = Input.touches[0]; //Check if the phase of that touch equals Began if (myTouch.phase == TouchPhase.Began) { //If so, set touchOrigin to the position of that touch touchOrigin = myTouch.position; } //If the touch phase is not Began, and instead is equal to Ended and the x of touchOrigin is greater or equal to zero: else if (myTouch.phase == TouchPhase.Ended && touchOrigin.x >= 0) { //Set touchEnd to equal the position of this touch Vector2 touchEnd = myTouch.position; //Calculate the difference between the beginning and end of the touch on the x axis. float x = touchEnd.x - touchOrigin.x; //Calculate the difference between the beginning and end of the touch on the y axis. float y = touchEnd.y - touchOrigin.y; //Set touchOrigin.x to -1 so that our else if statement will evaluate false and not repeat immediately. touchOrigin.x = -1; //Check if the difference along the x axis is greater than the difference along the y axis. if (Mathf.Abs(x) > Mathf.Abs(y)) //If x is greater than zero, set horizontal to 1, otherwise set it to -1 horizontal = x > 0 ? 1 : -1; else //If y is greater than zero, set horizontal to 1, otherwise set it to -1 vertical = y > 0 ? 1 : -1; } } #endif //End of mobile platform dependendent compilation section started above with #elif //Check if we have a non-zero value for horizontal or vertical if (horizontal != 0 || vertical != 0) { //Call AttemptMove passing in the generic parameter Wall, since that is what Player may interact with if they encounter one (by attacking it) //Pass in horizontal and vertical as parameters to specify the direction to move Player in. AttemptMove<Wall>(horizontal, vertical); } } //AttemptMove overrides the AttemptMove function in the base class MovingObject //AttemptMove takes a generic parameter T which for Player will be of the type Wall, it also takes integers for x and y direction to move in. protected override void AttemptMove<T>(int xDir, int yDir) { //Every time player moves, subtract from food points total. food--; //Update food text display to reflect current score. foodText.text = "Food: " + food; //Call the AttemptMove method of the base class, passing in the component T (in this case Wall) and x and y direction to move. base.AttemptMove<T>(xDir, yDir); //Hit allows us to reference the result of the Linecast done in Move. RaycastHit2D hit; //If Move returns true, meaning Player was able to move into an empty space. if (Move(xDir, yDir, out hit)) { //Call RandomizeSfx of SoundManager to play the move sound, passing in two audio clips to choose from. SoundManager.instance.RandomizeSfx(moveSound1, moveSound2); } //Since the player has moved and lost food points, check if the game has ended. CheckIfGameOver(); //Set the playersTurn boolean of GameManager to false now that players turn is over. GameManager.instance.playersTurn = false; } //OnCantMove overrides the abstract function OnCantMove in MovingObject. //It takes a generic parameter T which in the case of Player is a Wall which the player can attack and destroy. protected override void OnCantMove<T>(T component) { //Set hitWall to equal the component passed in as a parameter. Wall hitWall = component as Wall; //Call the DamageWall function of the Wall we are hitting. hitWall.DamageWall(wallDamage); //Set the attack trigger of the player's animation controller in order to play the player's attack animation. animator.SetTrigger("playerChop"); } //OnTriggerEnter2D is sent when another object enters a trigger collider attached to this object (2D physics only). private void OnTriggerEnter2D(Collider2D other) { //Check if the tag of the trigger collided with is Exit. if (other.tag == "Exit") { //Invoke the Restart function to start the next level with a delay of restartLevelDelay (default 1 second). Invoke("Restart", restartLevelDelay); //Disable the player object since level is over. enabled = false; } //Check if the tag of the trigger collided with is Food. else if (other.tag == "Food") { //Add pointsPerFood to the players current food total. food += pointsPerFood; //Update foodText to represent current total and notify player that they gained points foodText.text = "+" + pointsPerFood + " Food: " + food; //Call the RandomizeSfx function of SoundManager and pass in two eating sounds to choose between to play the eating sound effect. SoundManager.instance.RandomizeSfx(eatSound1, eatSound2); //Disable the food object the player collided with. other.gameObject.SetActive(false); } //Check if the tag of the trigger collided with is Soda. else if (other.tag == "Soda") { //Add pointsPerSoda to players food points total food += pointsPerSoda; //Update foodText to represent current total and notify player that they gained points foodText.text = "+" + pointsPerSoda + " Food: " + food; //Call the RandomizeSfx function of SoundManager and pass in two drinking sounds to choose between to play the drinking sound effect. SoundManager.instance.RandomizeSfx(drinkSound1, drinkSound2); //Disable the soda object the player collided with. other.gameObject.SetActive(false); } } //Restart reloads the scene when called. private void Restart() { //Load the last scene loaded, in this case Main, the only scene in the game.传入最后一个场景,在本例中只有一个场景---Main; Application.LoadLevel(Application.loadedLevel); } //LoseFood is called when an enemy attacks the player. //It takes a parameter loss which specifies how many points to lose.传入loss参数,用于具体说明失去多少分; public void LoseFood(int loss) { //Set the trigger for the player animator to transition to the playerHit animation. animator.SetTrigger("playerHit"); //Subtract lost food points from the players total. food -= loss; //Update the food display with the new total. foodText.text = "-" + loss + " Food: " + food; //Check to see if game has ended. CheckIfGameOver(); } //CheckIfGameOver checks if the player is out of food points and if so, ends the game. private void CheckIfGameOver() { //Check if food point total is less than or equal to zero. if (food <= 0) { //Call the PlaySingle function of SoundManager and pass it the gameOverSound as the audio clip to play. SoundManager.instance.PlaySingle(gameOverSound); //Stop the background music. SoundManager.instance.musicSource.Stop(); //Call the GameOver function of GameManager. GameManager.instance.GameOver(); } } }
Enemy
using UnityEngine; using System.Collections; //Enemy inherits from MovingObject, our base class for objects that can move, Player also inherits from this. public class Enemy : MovingObject { public int playerDamage; //The amount of food points to subtract from the player when attacking. public AudioClip attackSound1; //First of two audio clips to play when attacking the player. public AudioClip attackSound2; //Second of two audio clips to play when attacking the player. private Animator animator; //Variable of type Animator to store a reference to the enemy's Animator component. private Transform target; //Transform to attempt to move toward each turn. private bool skipMove; //Boolean to determine whether or not enemy should skip a turn or move this turn. //Start overrides the virtual Start function of the base class. protected override void Start() { //Register this enemy with our instance of GameManager by adding it to a list of Enemy objects. //This allows the GameManager to issue movement commands. GameManager.instance.AddEnemyToList(this); //Get and store a reference to the attached Animator component. animator = GetComponent<Animator>(); //Find the Player GameObject using it's tag and store a reference to its transform component. target = GameObject.FindGameObjectWithTag("Player").transform; //Call the start function of our base class MovingObject. base.Start(); } //Override the AttemptMove function of MovingObject to include functionality needed for Enemy to skip turns. //See comments in MovingObject for more on how base AttemptMove function works. protected override void AttemptMove<T>(int xDir, int yDir) { //Check if skipMove is true, if so set it to false and skip this turn. if (skipMove) { skipMove = false; return; } //Call the AttemptMove function from MovingObject. base.AttemptMove<T>(xDir, yDir); //Now that Enemy has moved, set skipMove to true to skip next move. skipMove = true; } //MoveEnemy is called by the GameManger each turn to tell each Enemy to try to move towards the player. public void MoveEnemy() { //Declare variables for X and Y axis move directions, these range from -1 to 1. //These values allow us to choose between the cardinal directions: up, down, left and right. int xDir = 0; int yDir = 0; //If the difference in positions is approximately zero (Epsilon) do the following: if (Mathf.Abs(target.position.x - transform.position.x) < float.Epsilon) //If the y coordinate of the target's (player) position is greater than the y coordinate of this enemy's position set y direction 1 (to move up). If not, set it to -1 (to move down). yDir = target.position.y > transform.position.y ? 1 : -1; //If the difference in positions is not approximately zero (Epsilon) do the following: else //Check if target x position is greater than enemy's x position, if so set x direction to 1 (move right), if not set to -1 (move left). xDir = target.position.x > transform.position.x ? 1 : -1; //Call the AttemptMove function and pass in the generic parameter Player, because Enemy is moving and expecting to potentially encounter a Player AttemptMove<Player>(xDir, yDir); } //OnCantMove is called if Enemy attempts to move into a space occupied by a Player, it overrides the OnCantMove function of MovingObject //and takes a generic parameter T which we use to pass in the component we expect to encounter, in this case Player protected override void OnCantMove<T>(T component) { //Declare hitPlayer and set it to equal the encountered component. Player hitPlayer = component as Player; //Call the LoseFood function of hitPlayer passing it playerDamage, the amount of foodpoints to be subtracted. hitPlayer.LoseFood(playerDamage); //Set the attack trigger of animator to trigger Enemy attack animation. animator.SetTrigger("enemyAttack"); //Call the RandomizeSfx function of SoundManager passing in the two audio clips to choose randomly between. SoundManager.instance.RandomizeSfx(attackSound1, attackSound2); } }
Wall 墙,障碍物,关卡trigger;
using UnityEngine; using System.Collections; public class Wall : MonoBehaviour { public AudioClip chopSound1; //1 of 2 audio clips that play when the wall is attacked by the player. public AudioClip chopSound2; //2 of 2 audio clips that play when the wall is attacked by the player. public Sprite dmgSprite; //Alternate sprite to display after Wall has been attacked by player. public int hp = 3; //hit points for the wall. private SpriteRenderer spriteRenderer; //Store a component reference to the attached SpriteRenderer. void Awake() { //Get a component reference to the SpriteRenderer. spriteRenderer = GetComponent<SpriteRenderer>(); } //DamageWall is called when the player attacks a wall. public void DamageWall(int loss) { //Call the RandomizeSfx function of SoundManager to play one of two chop sounds. SoundManager.instance.RandomizeSfx(chopSound1, chopSound2); //Set spriteRenderer to the damaged wall sprite. spriteRenderer.sprite = dmgSprite; //Subtract loss from hit point total. hp -= loss; //If hit points are less than or equal to zero: if (hp <= 0) //Disable the gameObject. gameObject.SetActive(false); } }
SoundManager 音乐和音效的管理;
using UnityEngine; using System.Collections; public class SoundManager : MonoBehaviour { public AudioSource efxSource; //Drag a reference to the audio source which will play the sound effects. public AudioSource musicSource; //Drag a reference to the audio source which will play the music. public static SoundManager instance = null; //Allows other scripts to call functions from SoundManager. public float lowPitchRange = .95f; //The lowest a sound effect will be randomly pitched. public float highPitchRange = 1.05f; //The highest a sound effect will be randomly pitched. void Awake() { //Check if there is already an instance of SoundManager if (instance == null) //if not, set it to this. instance = this; //If instance already exists: else if (instance != this) //Destroy this, this enforces our singleton pattern so there can only be one instance of SoundManager. Destroy(gameObject); //Set SoundManager to DontDestroyOnLoad so that it won't be destroyed when reloading our scene. DontDestroyOnLoad(gameObject); } //Used to play single sound clips. public void PlaySingle(AudioClip clip) { //Set the clip of our efxSource audio source to the clip passed in as a parameter. efxSource.clip = clip; //Play the clip. efxSource.Play(); } //RandomizeSfx chooses randomly between various audio clips and slightly changes their pitch. public void RandomizeSfx(params AudioClip[] clips) { //Generate a random number between 0 and the length of our array of clips passed in. int randomIndex = Random.Range(0, clips.Length); //Choose a random pitch to play back our clip at between our high and low pitch ranges. float randomPitch = Random.Range(lowPitchRange, highPitchRange); //Set the pitch of the audio source to the randomly chosen pitch. efxSource.pitch = randomPitch; //Set the clip to the clip at our randomly chosen index. efxSource.clip = clips[randomIndex]; //Play the clip. efxSource.Play(); } }
相关文章推荐
- unityshader固定管线的一些概念理解
- Unity入门游戏太空大战
- Unity中的行为树插件 Behavior Designer
- 如何在unity游戏运行过程中给GameObject添加一个组件
- 关于Unity3D中NavMesh报错问题
- Unity3d5.1简单配置及粒子效果的删除
- Unity入口函数
- GameUnity 2.0 文档(四) 网格+四叉树 最优碰撞检测
- unity canvas text显示及其角色伤害TextMesh显示
- unity中的简单的协程用法。
- uLua学习笔记(三):Unity3D和Lua之间的相互调用
- 【整理】unity3d优化总结篇
- DoTween的小技巧
- 用Sublime Text 2 作IDE开发 Unity3D 项目(VinceLi)
- Unity3D 中 点乘和叉乘的应用
- 简单Unity时间架构设计(克洛诺斯之匙)
- Unity中创建二维码
- UNITY3D学习笔记14
- 【猫猫的Unity Shader之旅】之Vertex&Fragment Shader下的透明
- unity, UGUI Text fadeIn