{"id":5103,"date":"2021-08-14T12:25:55","date_gmt":"2021-08-14T04:25:55","guid":{"rendered":"https:\/\/yanjingang.com\/blog\/?p=5103"},"modified":"2021-08-20T17:22:09","modified_gmt":"2021-08-20T09:22:09","slug":"%e5%b0%8f%e7%8c%aa%e5%ad%a6u3d-%e5%a1%94%e9%98%b2-%e6%95%8c%e4%ba%batowerdefense-maze","status":"publish","type":"post","link":"https:\/\/yanjingang.com\/blog\/?p=5103","title":{"rendered":"\u5c0f\u732a\u5b66U3D\u2014\u5854\u9632-\u654c\u4eba(TowerDefense-Enemies)"},"content":{"rendered":"<p>\u4e0a\u4e00\u8282\uff0c\u6211\u4eec\u8bb2\u89e3\u4e86\u5982\u4f55\u521b\u5efa\u6e38\u620f\u5730\u56fe\u3001\u76ee\u7684\u5730\u548c\u5899\u7684\u653e\u7f6e\u3001\u5bfb\u8def\u3002\u672c\u8282\u5c06\u91cd\u70b9\u5b66\u4e60\u5982\u4f55\u5728\u5730\u56fe\u4e2d\u6dfb\u52a0\u51fa\u751f\u70b9\u3001\u654c\u4eba\uff0c\u5e76\u8ba9\u654c\u4eba\u6cbf\u7740\u89c4\u5212\u8def\u5f84\u524d\u5f80\u79bb\u4ed6\u6700\u8fd1\u7684\u76ee\u7684\u5730\u3002<\/p>\n<h1>\u654c\u4eba\uff08Enemies\uff09<\/h1>\n<p><a href=\"https:\/\/yanjingang.com\/blog\/wp-content\/uploads\/2021\/08\/tower-defense-enemies.jpg\"><img loading=\"lazy\" class=\"alignnone size-full wp-image-5021\" src=\"https:\/\/yanjingang.com\/blog\/wp-content\/uploads\/2021\/08\/tower-defense-enemies.jpg\" alt=\"\" width=\"1024\" height=\"512\" srcset=\"https:\/\/yanjingang.com\/blog\/wp-content\/uploads\/2021\/08\/tower-defense-enemies.jpg 1024w, https:\/\/yanjingang.com\/blog\/wp-content\/uploads\/2021\/08\/tower-defense-enemies-300x150.jpg 300w, https:\/\/yanjingang.com\/blog\/wp-content\/uploads\/2021\/08\/tower-defense-enemies-768x384.jpg 768w, https:\/\/yanjingang.com\/blog\/wp-content\/uploads\/2021\/08\/tower-defense-enemies-624x312.jpg 624w\" sizes=\"(max-width: 1024px) 100vw, 1024px\" \/><\/a><\/p>\n<h2>1.\u51fa\u751f\u70b9\uff08Spawn Points\uff09<\/h2>\n<p>\u5728\u4ea7\u751f\u654c\u4eba\u4e4b\u524d\uff0c\u6211\u4eec\u9700\u8981\u786e\u5b9a\u5c06\u654c\u4eba\u653e\u7f6e\u5728\u677f\u4e0a\u7684\u54ea\u4e2a\u4f4d\u7f6e\u3002 \u6240\u4ee5\u9700\u8981\u521b\u5efa\u4e00\u4e2a\u51fa\u751f\u70b9\u3002<\/p>\n<h3>1.1.\u74e6\u7247\u5185\u5bb9\uff08Tile Content\uff09<\/h3>\n<p>a.\u751f\u6210\u70b9\u662f\u74e6\u7247\u5185\u5bb9\u7684\u53e6\u4e00\u79cd\u7c7b\u578b\uff0c\u5c06\u5176\u6dfb\u52a0\u5230GameTileContentType<\/p>\n<pre class=\"pure-highlightjs\"><code class=\"\">\/\/ \u74e6\u7247\u5185\u5bb9\u7c7b\u578b\r\npublic enum GameTileContentType{\r\n  Empty,  \/\/\u8def\r\n  Destination,  \/\/\u76ee\u7684\u5730\r\n  Wall,  \/\/\u5899\r\n  SpawnPoint  \/\/\u654c\u4eba\u751f\u6210\u70b9\r\n}<\/code><\/pre>\n<p>b.\u521b\u5efa\u4e00\u4e2a\u9884\u5236\u4ef6\u4ee5\u4f7f\u5176\u53ef\u89c6\u5316\uff08\u53c2\u8003\u76ee\u7684\u5730\u9884\u5236\u4ef6\uff0c\u6539\u4e2a\u989c\u8272\u5c31\u884c\uff09<\/p>\n<p><a href=\"https:\/\/yanjingang.com\/blog\/wp-content\/uploads\/2021\/08\/spawn-point-inspector.png\"><img loading=\"lazy\" class=\"alignnone size-full wp-image-5116\" src=\"https:\/\/yanjingang.com\/blog\/wp-content\/uploads\/2021\/08\/spawn-point-inspector.png\" alt=\"\" width=\"640\" height=\"208\" srcset=\"https:\/\/yanjingang.com\/blog\/wp-content\/uploads\/2021\/08\/spawn-point-inspector.png 640w, https:\/\/yanjingang.com\/blog\/wp-content\/uploads\/2021\/08\/spawn-point-inspector-300x98.png 300w, https:\/\/yanjingang.com\/blog\/wp-content\/uploads\/2021\/08\/spawn-point-inspector-624x203.png 624w\" sizes=\"(max-width: 640px) 100vw, 640px\" \/><\/a><\/p>\n<p>c.\u5c06\u5bf9\u51fa\u751f\u70b9\u7684\u652f\u6301\u6dfb\u52a0\u5230\u5185\u5bb9\u5de5\u5382\uff0c\u5e76\u4e3a\u5176\u63d0\u4f9b\u5bf9\u9884\u5236\u4ef6\u7684\u5f15\u7528\u3002<\/p>\n<pre class=\"pure-highlightjs\"><code class=\"\">\/\/ \u74e6\u7247\u5185\u5bb9\u5de5\u5382\r\npublic class GameTileContentFactory : ScriptableObject\r\n{\r\n    ...\r\n    [SerializeField]\r\n    GameTileContent spawnPointPrefab = default;  \/\/ \u751f\u6210\u70b9\u9884\u5236\u4ef6\r\n    ...\r\n    \/\/ \u751f\u6210\u4e0d\u540c\u7c7b\u578b\u74e6\u7247\r\n    public GameTileContent Get(GameTileContentType type){\r\n      switch(type){\r\n         ...\r\n        case GameTileContentType.SpawnPoint:\r\n          return Get(spawnPointPrefab);\r\n      }\r\n      return null;\r\n    }\r\n    ...\r\n}\r\n<\/code><\/pre>\n<p><a href=\"https:\/\/yanjingang.com\/blog\/wp-content\/uploads\/2021\/08\/factory-spawnpoint.png\"><img loading=\"lazy\" class=\"alignnone size-full wp-image-5117\" src=\"https:\/\/yanjingang.com\/blog\/wp-content\/uploads\/2021\/08\/factory-spawnpoint.png\" alt=\"\" width=\"640\" height=\"284\" srcset=\"https:\/\/yanjingang.com\/blog\/wp-content\/uploads\/2021\/08\/factory-spawnpoint.png 640w, https:\/\/yanjingang.com\/blog\/wp-content\/uploads\/2021\/08\/factory-spawnpoint-300x133.png 300w, https:\/\/yanjingang.com\/blog\/wp-content\/uploads\/2021\/08\/factory-spawnpoint-624x277.png 624w\" sizes=\"(max-width: 640px) 100vw, 640px\" \/><\/a><\/p>\n<h3>1.2.\u5207\u6362\u51fa\u751f\u70b9\uff08Toggling Spawn Points\uff09<\/h3>\n<p>a.\u4e0e\u5176\u4ed6\u5207\u6362\u65b9\u6cd5\u4e00\u6837\uff0c\u6dfb\u52a0\u4e00\u79cd\u5c06\u751f\u6210\u70b9\u5207\u6362\u5230GameBoard\u7684\u65b9\u6cd5\uff0c\u5e76\u8bbe\u7f6e\u4e00\u4e2a\u9ed8\u8ba4\u7684\u51fa\u751f\u70b9<\/p>\n<p>\u6e38\u620f\u53ea\u6709\u5728\u6709\u654c\u4eba\u7684\u60c5\u51b5\u4e0b\u624d\u6709\u610f\u4e49\uff0c\u8fd9\u5c31\u9700\u8981\u6709\u51fa\u751f\u70b9\u3002 \u56e0\u6b64\uff0c\u6709\u6548\u7684\u6e38\u620f\u9762\u677f\u5e94\u81f3\u5c11\u5305\u542b\u4e00\u4e2a\u51fa\u751f\u70b9\u3002 \u6dfb\u52a0\u654c\u4eba\u65f6\uff0c\u6211\u4eec\u7a0d\u540e\u8fd8\u9700\u8981\u8bbf\u95ee\u51fa\u751f\u70b9\uff0c\u56e0\u6b64\u4f7f\u7528\u5217\u8868\u6765\u8ddf\u8e2a\u6240\u6709\u5e26\u6709\u51fa\u751f\u70b9\u7684\u74e6\u7247\u3002 \u5207\u6362\u51fa\u751f\u70b9\u65f6\u66f4\u65b0\u5217\u8868\uff0c\u5e76\u9632\u6b62\u5220\u9664\u6700\u540e\u4e00\u4e2a\u51fa\u751f\u70b9\u3002<\/p>\n<pre class=\"pure-highlightjs\"><code class=\"\">\/\/ \u6e38\u620f\u9762\u677f\u5bf9\u8c61\r\npublic class GameBoard : MonoBehaviour\r\n{\r\n    ...\r\n    List&lt;GameTile&gt; spawnPoints = new List&lt;GameTile&gt;();  \/\/\u51fa\u751f\u70b9\u5217\u8868\r\n\r\n    \/\/ \u521d\u59cb\u5316\u65b9\u6cd5\r\n    public void Initialize(Vector2Int size, GameTileContentFactory contentFactory){\r\n      ...\r\n      \/\/ \u8bbe\u7f6e\u51fa\u751f\u70b9\r\n      ToggleSpawnPoint(tiles[0]);\r\n    }\r\n    ...\r\n    \/\/ \u5bf9\u8c03\u74e6\u7247\u7684\u751f\u6210\u70b9&lt;-&gt;\u975e\u751f\u6210\u70b9\u5c5e\u6027\r\n    public void ToggleSpawnPoint(GameTile tile){\r\n      if(tile == null){\r\n        return;\r\n      }\r\n      if(tile.Content.Type == GameTileContentType.SpawnPoint){\r\n        if(spawnPoints.Count &gt; 1){\r\n          spawnPoints.Remove(tile); \/\/\u4ece\u5217\u8868\u4e2d\u79fb\u9664\r\n          tile.Content = contentFactory.Get(GameTileContentType.Empty);\r\n        }\r\n      }else if(tile.Content.Type == GameTileContentType.Empty){ \/\/\u4ec5\u652f\u6301\u5728\u7a7a\u74e6\u7247\u548c\u51fa\u751f\u5730\u4e4b\u95f4\u5207\u6362\r\n        tile.Content = contentFactory.Get(GameTileContentType.SpawnPoint);\r\n        spawnPoints.Add(tile); \/\/\u6dfb\u52a0\u5230\u51fa\u751f\u70b9\u5217\u8868\r\n      }\r\n    }\r\n    ...\r\n}\r\n<\/code><\/pre>\n<p>b.\u5728Game\u4e2d\u6dfb\u52a0\u9f20\u6807\u53f3\u952e+Shit\u4e8b\u4ef6\uff0c\u7528\u4e8e\u8bbe\u7f6e\u51fa\u751f\u70b9<\/p>\n<pre class=\"pure-highlightjs\"><code class=\"\">\/\/ \u6e38\u620f\u5bf9\u8c61\r\npublic class Game : MonoBehaviour\r\n{\r\n    ...\r\n    void Update(){\r\n      if(Input.GetMouseButtonDown(1)){  \/\/\u9f20\u6807\u53f3\u952e\u4e8b\u4ef6\r\n        if(Input.GetKey(KeyCode.LeftShift)){  \/\/\u540c\u65f6\u6309\u4e0b\u4e86Shift\r\n          board.ToggleSpawnPoint(board.GetTile(TouchRay));  \/\/\u5207\u6362\u7a7a\u74e6\u7247\u548c\u51fa\u751f\u5730\r\n        }else{\r\n          board.ToggleDestination(board.GetTile(TouchRay));  \/\/\u5207\u6362\u7a7a\u74e6\u7247\u548c\u76ee\u7684\u5730\r\n        }\r\n      }\r\n      ...\r\n    }\r\n}\r\n<\/code><\/pre>\n<p>c.\u8fd0\u884c\u5e76\u6d4b\u8bd5\u6dfb\u52a0\u51fa\u751f\u70b9<\/p>\n<p><a href=\"https:\/\/yanjingang.com\/blog\/wp-content\/uploads\/2021\/08\/board-with-spawn-points.png\"><img loading=\"lazy\" class=\"alignnone size-full wp-image-5118\" src=\"https:\/\/yanjingang.com\/blog\/wp-content\/uploads\/2021\/08\/board-with-spawn-points.png\" alt=\"\" width=\"460\" height=\"460\" srcset=\"https:\/\/yanjingang.com\/blog\/wp-content\/uploads\/2021\/08\/board-with-spawn-points.png 460w, https:\/\/yanjingang.com\/blog\/wp-content\/uploads\/2021\/08\/board-with-spawn-points-150x150.png 150w, https:\/\/yanjingang.com\/blog\/wp-content\/uploads\/2021\/08\/board-with-spawn-points-300x300.png 300w\" sizes=\"(max-width: 460px) 100vw, 460px\" \/><\/a><\/p>\n<h3>1.3.\u8bbf\u95ee\u51fa\u751f\u70b9\uff08Accessing Spawn Points\uff09<\/h3>\n<pre class=\"pure-highlightjs\"><code class=\"\">\/\/ \u6e38\u620f\u9762\u677f\u5bf9\u8c61\r\npublic class GameBoard : MonoBehaviour\r\n{\r\n    ...\r\n    \/\/ \u83b7\u53d6\u5355\u4e2a\u51fa\u751f\u70b9\r\n    public GameTile GetSpawnPoint(int i){\r\n      return spawnPoints[i];\r\n    }\r\n    \/\/ \u83b7\u53d6\u51fa\u751f\u70b9\u603b\u6570\r\n    public int SpawnPointCount =&gt; spawnPoints.Count;\r\n}\r\n<\/code><\/pre>\n<h2>2.\u751f\u6210\u654c\u4eba\uff08Spawning Enemies\uff09<\/h2>\n<p>\u751f\u6210\u654c\u4eba\u6709\u70b9\u50cf\u521b\u5efa\u74e6\u7247\u5185\u5bb9\u3002 \u6211\u4eec\u901a\u8fc7\u5de5\u5382\u521b\u5efa\u4e00\u4e2a\u9884\u5236\u5b9e\u4f8b\uff0c\u7136\u540e\u5c06\u5176\u653e\u5728\u677f\u4e0a\u3002<\/p>\n<h3>2.1.\u5de5\u5382\uff08Factories\uff09<\/h3>\n<p>a.\u521b\u5efa\u901a\u7528\u6e38\u620f\u5bf9\u8c61\u5de5\u5382\uff0c\u5e76\u66ff\u6362\u539f\u74e6\u7247\u5185\u5bb9\u5de5\u5382\u7684MoveToFactoryScene\u5bf9\u8c61\u79fb\u5165\u573a\u666f\u65b9\u6cd5<\/p>\n<pre class=\"pure-highlightjs\"><code class=\"\">using System.Collections;\r\nusing System.Collections.Generic;\r\nusing UnityEngine;\r\nusing UnityEngine.SceneManagement;\r\n\r\n\/\/ \u6e38\u620f\u5bf9\u8c61\u5de5\u5382\r\npublic abstract class GameObjectFactory : ScriptableObject\r\n{\r\n  Scene scene;  \/\/\u5de5\u5382\u7684\u5185\u5bb9\u573a\u666f\r\n\r\n  \/\/ \u521b\u5efa\u9884\u5236\u4ef6\u5bf9\u8c61\u5b9e\u4f8b\uff0c\u5e76\u653e\u5165\u5de5\u5382\u573a\u666f\r\n  protected T CreateGameObjectInstance&lt;T&gt;(T prefab) where T : MonoBehaviour{\r\n    if(!scene.isLoaded){\r\n      if(Application.isEditor){ \/\/\u5728\u7f16\u8f91\u5668\u4e2d\u65f6\uff0c\u68c0\u67e5\u573a\u666f\u662f\u5426\u5b58\u5728\r\n        scene = SceneManager.GetSceneByName(name);\r\n        if(!scene.isLoaded){\r\n          scene = SceneManager.CreateScene(name);\r\n        }\r\n      }else{\r\n        scene = SceneManager.CreateScene(name);\r\n      }\r\n    }\r\n    T o = Instantiate(prefab);\r\n    SceneManager.MoveGameObjectToScene(o.gameObject, scene);\r\n    return o;\r\n  }\r\n}\r\n\r\n\r\nusing UnityEngine;\r\n\/\/using UnityEngine.SceneManagement;\r\n\r\n\/\/ \u74e6\u7247\u5185\u5bb9\u5de5\u5382\r\n[CreateAssetMenu]\r\npublic class GameTileContentFactory : GameObjectFactory {\r\n\t\u2026\r\n\t\/\/Scene contentScene;\r\n\t\u2026\r\n\r\n\tGameTileContent Get (GameTileContent prefab) {\r\n\t\tGameTileContent instance = CreateGameObjectInstance(prefab);\r\n\t\tinstance.OriginFactory = this;\r\n\t\t\/\/MoveToFactoryScene(instance.gameObject);\r\n\t\treturn instance;\r\n\t}\r\n\r\n\t\/\/void MoveToFactoryScene (GameObject o) {\r\n\t\/\/\t\u2026\r\n\t\/\/}\r\n}<\/code><\/pre>\n<p>b.\u521b\u5efaEnemyFactory\uff0c\u901a\u8fc7Get\u65b9\u6cd5\u5b9e\u4f8b\u5316\u4e00\u4e2a\u654c\u4eba\u9884\u5236\u4ef6\uff0c\u4ee5\u53ca\u4e00\u4e2a\u76f8\u5e94\u7684\u56de\u6536\u65b9\u6cd5\u3002<\/p>\n<pre class=\"pure-highlightjs\"><code class=\"\">using System.Collections;\r\nusing System.Collections.Generic;\r\nusing UnityEngine;\r\n\r\n\/\/ \u654c\u4eba\u9884\u5236\u4ef6\u5de5\u5382\r\n[CreateAssetMenu] \/\/\u6dfb\u52a0\u5230unity create\u83dc\u5355\r\npublic class EnemyFactory : GameObjectFactory{\r\n    [SerializeField]\r\n    Enemy prefab = default;\r\n\r\n    \/\/ \u751f\u6210\u654c\u4eba\r\n    public Enemy Get(){\r\n      Enemy o = CreateGameObjectInstance(prefab);\r\n      o.OriginFactory = this;\r\n      return o;\r\n    }\r\n\r\n    \/\/ \u56de\u6536\u654c\u4eba\r\n    public void Reclaim(Enemy o){\r\n      Debug.Assert(o.OriginFactory == this, \"Wrong factory reclaimed!\");\r\n      Destroy(o.gameObject);\r\n    }\r\n}\r\n<\/code><\/pre>\n<p>c.\u65b0\u7684\u654c\u4eba\u7c7b\u578b\u4ec5\u9700\u8981\u8ffd\u8e2a\u5176\u539f\u59cb\u5de5\u5382<\/p>\n<pre class=\"pure-highlightjs\"><code class=\"\">using System.Collections;\r\nusing System.Collections.Generic;\r\nusing UnityEngine;\r\n\r\n\/\/ \u654c\u4eba\r\npublic class Enemy : MonoBehaviour\r\n{\r\n    EnemyFactory originFactory;\r\n\r\n    public EnemyFactory OriginFactory{\r\n      get =&gt; originFactory;\r\n      set {\r\n        Debug.Assert(originFactory == null, \"Redefined origin factory!\");\r\n        originFactory = value;\r\n      }\r\n    }\r\n}\r\n<\/code><\/pre>\n<h3>2.2.\u9884\u5236\u4f53\uff08Prefab\uff09<\/h3>\n<p>\u654c\u4eba\u9700\u8981\u53ef\u89c6\u5316\uff0c\u5e76\u4e14\u53ef\u4ee5\u662f\u4efb\u4f55\u4e1c\u897f\u3002 \u6211\u4eec\u5c06\u4f7f\u7528\u673a\u5668\u4eba\uff0c\u8718\u86db\uff0c\u9b3c\u9b42\u6216\u8bf8\u5982\u7acb\u65b9\u4f53\u4e4b\u7c7b\u7684\u7b80\u5355\u5bf9\u8c61\u3002 \u4f46\u603b\u7684\u6765\u8bf4\uff0c\u654c\u4eba\u62e5\u6709\u4efb\u610f\u590d\u6742\u76843D\u6a21\u578b\u3002<\/p>\n<p>a.\u4e3a\u654c\u4eba\u7684\u9884\u5236\u5c42\u521b\u5efa\u4e00\u4e2a\u6839\u5bf9\u8c61\uff0c\u8be5\u6839\u5bf9\u8c61\u4ec5\u9644\u52a0\u4e86Enemy\u7ec4\u4ef6\u3002<\/p>\n<p><a href=\"https:\/\/yanjingang.com\/blog\/wp-content\/uploads\/2021\/08\/prefab-root.png\"><img loading=\"lazy\" class=\"alignnone size-full wp-image-5123\" src=\"https:\/\/yanjingang.com\/blog\/wp-content\/uploads\/2021\/08\/prefab-root.png\" alt=\"\" width=\"640\" height=\"320\" srcset=\"https:\/\/yanjingang.com\/blog\/wp-content\/uploads\/2021\/08\/prefab-root.png 640w, https:\/\/yanjingang.com\/blog\/wp-content\/uploads\/2021\/08\/prefab-root-300x150.png 300w, https:\/\/yanjingang.com\/blog\/wp-content\/uploads\/2021\/08\/prefab-root-624x312.png 624w\" sizes=\"(max-width: 640px) 100vw, 640px\" \/><\/a><\/p>\n<p>b.\u7ed9\u8be5\u5bf9\u8c61\u4e00\u4e2aModel\u5b50\u8282\u70b9\uff0c\u5373\u6a21\u578b\u6839<\/p>\n<p><a href=\"https:\/\/yanjingang.com\/blog\/wp-content\/uploads\/2021\/08\/model-root.png\"><img loading=\"lazy\" class=\"alignnone size-large wp-image-5125\" src=\"https:\/\/yanjingang.com\/blog\/wp-content\/uploads\/2021\/08\/model-root.png\" alt=\"\" width=\"625\" height=\"234\" srcset=\"https:\/\/yanjingang.com\/blog\/wp-content\/uploads\/2021\/08\/model-root.png 640w, https:\/\/yanjingang.com\/blog\/wp-content\/uploads\/2021\/08\/model-root-300x113.png 300w, https:\/\/yanjingang.com\/blog\/wp-content\/uploads\/2021\/08\/model-root-624x234.png 624w\" sizes=\"(max-width: 625px) 100vw, 625px\" \/><\/a><\/p>\n<p>\u6a21\u578b\u6839\u7684\u76ee\u7684\u662f\u76f8\u5bf9\u4e8e\u654c\u4eba\u7684\u5c40\u90e8\u539f\u70b9\u5b9a\u4f4d3D\u6a21\u578b\uff0c\u56e0\u6b64\u5c06\u5176\u89c6\u4e3a\u5176\u7ad9\u7acb\u6216\u60ac\u505c\u5728\u5176\u4e0a\u65b9\u7684\u67a2\u8f74\u70b9\u3002 c.\u672c\u4f8b\u4e2d\uff0c\u6a21\u578b\u662f\u9ed8\u8ba4\u6bd4\u4f8b\u7684\u7acb\u65b9\u4f53\uff0c\u6211\u5c06\u5176\u8bbe\u7f6e\u4e3a\u6df1\u84dd\u8272\u3002 \u4f7f\u5b83\u6210\u4e3a\u6a21\u578b\u6839\u7684\u5b50\u8282\u70b9\uff0c\u5e76\u5c06\u5176Y\u4f4d\u7f6e\u8bbe\u7f6e\u4e3a0.25\uff0c\u4ee5\u4fbf\u5b83\u4f4d\u4e8e\u5730\u9762\u4e0a<\/p>\n<p><a href=\"https:\/\/yanjingang.com\/blog\/wp-content\/uploads\/2021\/08\/cube.png\"><img loading=\"lazy\" class=\"alignnone size-large wp-image-5124\" src=\"https:\/\/yanjingang.com\/blog\/wp-content\/uploads\/2021\/08\/cube.png\" alt=\"\" width=\"625\" height=\"402\" srcset=\"https:\/\/yanjingang.com\/blog\/wp-content\/uploads\/2021\/08\/cube.png 640w, https:\/\/yanjingang.com\/blog\/wp-content\/uploads\/2021\/08\/cube-300x193.png 300w, https:\/\/yanjingang.com\/blog\/wp-content\/uploads\/2021\/08\/cube-624x402.png 624w\" sizes=\"(max-width: 625px) 100vw, 625px\" \/><\/a><\/p>\n<p>\u56e0\u6b64\uff0c\u654c\u4eba\u7684\u9884\u5236\u4ef6\u7531\u4e09\u4e2a\u5d4c\u5957\u5bf9\u8c61\u7ec4\u6210\uff1a\u9884\u5236\u6839\uff0c\u6a21\u578b\u6839\u548c\u7acb\u65b9\u4f53\u3002 \u5bf9\u4e8e\u7b80\u5355\u7684\u7acb\u65b9\u4f53\u800c\u8a00\uff0c\u8fd9\u53ef\u4ee5\u8ba4\u4e3a\u662f\u8fc7\u6e21\u8bbe\u8ba1\u4e86\uff0c\u4f46\u5b83\u53ef\u4ee5\u79fb\u52a8\u548c\u8bbe\u7f6e\u4efb\u4f55\u654c\u4eba\u7684\u52a8\u753b\u800c\u4e0d\u7528\u62c5\u5fc3\u5176\u7ec6\u8282\u3002<\/p>\n<p><a href=\"https:\/\/yanjingang.com\/blog\/wp-content\/uploads\/2021\/08\/prefab-hierarchy-.png\"><img loading=\"lazy\" class=\"alignnone size-large wp-image-5127\" src=\"https:\/\/yanjingang.com\/blog\/wp-content\/uploads\/2021\/08\/prefab-hierarchy-.png\" alt=\"\" width=\"450\" height=\"148\" srcset=\"https:\/\/yanjingang.com\/blog\/wp-content\/uploads\/2021\/08\/prefab-hierarchy-.png 450w, https:\/\/yanjingang.com\/blog\/wp-content\/uploads\/2021\/08\/prefab-hierarchy--300x99.png 300w\" sizes=\"(max-width: 450px) 100vw, 450px\" \/><\/a><\/p>\n<p>d.\u901a\u8fc7CreateAssetMenu\u521b\u5efa\u4e00\u4e2a\u654c\u4eba\u5de5\u5382\u5e76\u5c06\u9884\u5236\u4ef6\u5206\u914d\u7ed9\u5b83<\/p>\n<p><a href=\"https:\/\/yanjingang.com\/blog\/wp-content\/uploads\/2021\/08\/factory-.png\"><img loading=\"lazy\" class=\"alignnone size-full wp-image-5126\" src=\"https:\/\/yanjingang.com\/blog\/wp-content\/uploads\/2021\/08\/factory-.png\" alt=\"\" width=\"640\" height=\"176\" srcset=\"https:\/\/yanjingang.com\/blog\/wp-content\/uploads\/2021\/08\/factory-.png 640w, https:\/\/yanjingang.com\/blog\/wp-content\/uploads\/2021\/08\/factory--300x83.png 300w, https:\/\/yanjingang.com\/blog\/wp-content\/uploads\/2021\/08\/factory--624x172.png 624w\" sizes=\"(max-width: 640px) 100vw, 640px\" \/><\/a><\/p>\n<h3>2.3.\u6dfb\u52a0\u654c\u4eba\u5230\u5730\u56fe\uff08Placing Enemies on the Board\uff09<\/h3>\n<p>\u4e3a\u4e86\u5c06\u654c\u4eba\u653e\u5728\u9762\u677f\u4e0a\uff0c\u6e38\u620f\u9700\u8981\u5f15\u7528\u654c\u4eba\u5de5\u5382\u3002 \u7531\u4e8e\u6211\u4eec\u5c06\u9700\u8981\u5927\u91cf\u654c\u4eba\uff0c\u56e0\u6b64\u8fd8\u6dfb\u52a0\u4e86\u4e00\u4e2a\u751f\u6210\u901f\u5ea6\u7684\u914d\u7f6e\u9009\u9879\uff0c\u4ee5\u6bcf\u79d2\u654c\u4eba\u6570\u8868\u793a\u3002 0.1-10\u7684\u8303\u56f4\u4f3c\u4e4e\u662f\u5408\u7406\u7684\uff0c\u9ed8\u8ba4\u503c\u4e3a1\u3002<\/p>\n<p><a href=\"https:\/\/yanjingang.com\/blog\/wp-content\/uploads\/2021\/08\/game-enemy.png\"><img loading=\"lazy\" class=\"alignnone size-full wp-image-5128\" src=\"https:\/\/yanjingang.com\/blog\/wp-content\/uploads\/2021\/08\/game-enemy.png\" alt=\"\" width=\"640\" height=\"288\" srcset=\"https:\/\/yanjingang.com\/blog\/wp-content\/uploads\/2021\/08\/game-enemy.png 640w, https:\/\/yanjingang.com\/blog\/wp-content\/uploads\/2021\/08\/game-enemy-300x135.png 300w, https:\/\/yanjingang.com\/blog\/wp-content\/uploads\/2021\/08\/game-enemy-624x281.png 624w\" sizes=\"(max-width: 640px) 100vw, 640px\" \/><\/a><\/p>\n<p>\u901a\u8fc7\u5c06\u901f\u5ea6\u4e58\u4ee5\u65f6\u95f4\u589e\u91cf\u6765\u8ddf\u8e2aUpdate\u4e2d\u7684\u751f\u6210\u8fdb\u5ea6\u3002 \u5982\u679c\u8fdb\u5ea6\u8d85\u8fc71\uff0c\u5219\u9012\u51cf\u5e76\u901a\u8fc7\u65b0\u7684SpawnEnemy\u65b9\u6cd5\u751f\u6210\u654c\u4eba\u3002 \u53ea\u8981\u8fdb\u5ea6\u8d85\u8fc71\uff0c\u5c31\u7ee7\u7eed\u6267\u884c\u6b64\u64cd\u4f5c\uff0c\u4ee5\u9632\u901f\u5ea6\u8fc7\u5feb\u4e14\u5e27\u65f6\u95f4\u7ed3\u675f\u5f97\u592a\u957f\uff0c\u800c\u4ea7\u751f\u591a\u4e2a\u654c\u4eba\u3002<\/p>\n<pre class=\"pure-highlightjs\"><code class=\"\">\/\/ \u6e38\u620f\u5bf9\u8c61\r\npublic class Game : MonoBehaviour{\r\n    ...\r\n    float spawnProcess;                   \/\/\u654c\u4eba\u751f\u6210\u8fdb\u5ea6\r\n    ...\r\n    void Update(){\r\n        ...\r\n        \/\/ \u751f\u6210\u654c\u4eba\r\n        spawnProcess += spawnSpeed * Time.deltaTime;\r\n        while(spawnProcess &gt;= 1f){\r\n          spawnProcess -= 1f;\r\n          SpawnEnemy();\r\n        }\r\n    }\r\n    \/\/ \u751f\u6210\u654c\u4eba\r\n    void SpawnEnemy(){\r\n      GameTile spawnPoint = board.GetSpawnPoint(Random.Range(0, board.SpawnPointCount));\r\n      Enemy enemy = enemyFactory.Get();\r\n      enemy.SpawnOn(spawnPoint);\r\n    }\r\n    ...\r\n}<\/code><\/pre>\n<p>\u8ba9SpawnEnemy\u4ece\u68cb\u76d8\u4e0a\u968f\u673a\u83b7\u5f97\u4e00\u4e2a\u751f\u6210\u70b9\uff0c\u5e76\u5728\u8be5\u56fe\u5757\u4e0a\u751f\u6210\u4e00\u4e2a\u654c\u4eba\u3002 \u6211\u4eec\u5c06\u4e3a\u654c\u4eba\u63d0\u4f9b\u4e00\u4e2aSpawnOn\u65b9\u6cd5\u4ee5\u6b63\u786e\u5b9a\u4f4d\u81ea\u8eab\u3002<\/p>\n<pre class=\"pure-highlightjs\"><code class=\"\">\/\/ \u654c\u4eba\r\npublic class Enemy : MonoBehaviour\r\n{\r\n    ...\r\n    \/\/ \u8bbe\u7f6e\u654c\u4eba\u5230\u51fa\u751f\u74e6\u7247\u4f4d\u7f6e\r\n    public void SpawnOn(GameTile tile){\r\n      transform.localPosition = tile.transform.localPosition;\r\n    }\r\n}<\/code><\/pre>\n<p><a href=\"https:\/\/yanjingang.com\/blog\/wp-content\/uploads\/2021\/08\/spawned-enemies.png\"><img loading=\"lazy\" class=\"alignnone size-full wp-image-5129\" src=\"https:\/\/yanjingang.com\/blog\/wp-content\/uploads\/2021\/08\/spawned-enemies.png\" alt=\"\" width=\"460\" height=\"460\" srcset=\"https:\/\/yanjingang.com\/blog\/wp-content\/uploads\/2021\/08\/spawned-enemies.png 460w, https:\/\/yanjingang.com\/blog\/wp-content\/uploads\/2021\/08\/spawned-enemies-150x150.png 150w, https:\/\/yanjingang.com\/blog\/wp-content\/uploads\/2021\/08\/spawned-enemies-300x300.png 300w\" sizes=\"(max-width: 460px) 100vw, 460px\" \/><\/a><\/p>\n<p>&nbsp;<\/p>\n<h2>3.\u79fb\u52a8\u654c\u4eba\uff08Moving Enemies\uff09<\/h2>\n<p>\u4e00\u65e6\u654c\u4eba\u51fa\u73b0\uff0c\u5b83\u5e94\u8be5\u5f00\u59cb\u6cbf\u7740\u8def\u5f84\u79fb\u52a8\u5230\u6700\u8fd1\u7684\u76ee\u7684\u5730\u3002 \u6211\u4eec\u5fc5\u987b\u4e3a\u5b83\u4eec\u8bbe\u7f6e\u52a8\u753b\uff0c\u4ee5\u5b9e\u73b0\u8fd9\u4e00\u76ee\u6807\u3002 \u6211\u4eec\u9996\u5148\u7b80\u5355\u5730\u5c06\u5b83\u4eec\u5728\u56fe\u5757\u4e4b\u95f4\u6ed1\u52a8\uff0c\u7136\u540e\u4f7f\u5b83\u4eec\u7684\u79fb\u52a8\u66f4\u52a0\u590d\u6742\u3002<\/p>\n<h3>3.1.\u654c\u4eba\u96c6\u5408\uff08Enemy Collection\uff09<\/h3>\n<p>a.Game\u6dfb\u52a0\u654c\u4eba\u96c6\u5408\uff0c\u5e76\u5728Update\u4e2d\u81ea\u52a8\u79fb\u52a8\u654c\u4eba<\/p>\n<pre class=\"pure-highlightjs\"><code class=\"\">\/\/ \u6e38\u620f\u5bf9\u8c61\r\npublic class Game : MonoBehaviour{\r\n    ...\r\n    EnemyCollection enemies = new EnemyCollection();  \/\/\u654c\u4eba\u96c6\u5408\uff08\u7528\u4e8e\u63a7\u5236\u79fb\u52a8\u7b49\uff09\r\n    ...\r\n    void Update(){\r\n      ...\r\n      \/\/ \u66f4\u65b0\u654c\u4eba\u4f4d\u7f6e\u7b49\u4fe1\u606f\r\n      enemies.GameUpdate();\r\n    }\r\n    \/\/ \u751f\u6210\u654c\u4eba\r\n    void SpawnEnemy(){\r\n      ...\r\n      enemies.Add(enemy); \/\/\u6dfb\u52a0\u5230\u654c\u4eba\u96c6\u5408\u4e2d\u7528\u4e8e\u540e\u7eed\u8ddf\u8e2a\r\n    }\r\n}\r\n<\/code><\/pre>\n<p>b.\u6dfb\u52a0EnemyCollection\u654c\u4eba\u96c6\u5408\uff0c\u589e\u52a0\u6279\u91cf\u79fb\u52a8\u654c\u4eba\u65b9\u6cd5<\/p>\n<pre class=\"pure-highlightjs\"><code class=\"\">using System.Collections;\r\nusing System.Collections.Generic;\r\nusing UnityEngine;\r\n\r\n\/\/ \u654c\u4eba\u96c6\u5408\r\n[System.Serializable]\r\npublic class EnemyCollection{\r\n    List&lt;Enemy&gt; enemies = new List&lt;Enemy&gt;();\r\n\r\n    \/\/ \u6dfb\u52a0\u654c\u4eba\u5230\u96c6\u5408\r\n    public void Add(Enemy o){\r\n      enemies.Add(o);\r\n    }\r\n\r\n    \/\/ \u66f4\u65b0\u96c6\u5408\u4e2d\u6240\u6709\u654c\u4eba\u7684\u4f4d\u7f6e\u7b49\u4fe1\u606f\r\n    public void GameUpdate(){\r\n      for(int i=0; i&lt;enemies.Count; i++){\r\n        if(!enemies[i].GameUpdate()){ \/\/ \u654c\u4eba\u55dd\u5c41\u4e86\uff0c\u4ece\u96c6\u5408\u4e2d\u79fb\u9664\u5e76\u79fb\u52a8\u5c3e\u90e8\u7d22\u5f15\u51cf\u5c11\u788e\u7247\r\n          int lastIdx = enemies.Count - 1;\r\n          enemies[i] = enemies[lastIdx];\r\n          enemies.RemoveAt(lastIdx);\r\n        }\r\n      }\r\n    }\r\n}\r\n<\/code><\/pre>\n<p>c.\u654c\u4eba\u589e\u52a0\u5411\u524d\u79fb\u52a8\u65b9\u6cd5<\/p>\n<pre class=\"pure-highlightjs\"><code class=\"\">\/\/ \u654c\u4eba\r\npublic class Enemy : MonoBehaviour\r\n{\r\n    ...\r\n    \/\/ \u66f4\u65b0\u654c\u4eba\u4f4d\u7f6e\u7b49\u4fe1\u606f\uff08\u654c\u4eba\u6b7b\u4ea1\u65f6\u8fd4\u56defalse\uff09\r\n    public bool GameUpdate(){\r\n      transform.localPosition += Vector3.forward * Time.deltaTime;  \/\/\u6839\u636e\u65f6\u95f4\u5411\u524d\u79fb\u52a8\r\n      return true;\r\n    }\r\n}<\/code><\/pre>\n<p>d.\u8fd0\u884c\uff0cShift+\u9f20\u6807\u53f3\u952e\u6dfb\u52a0\u591a\u4e2a\u654c\u4eba\u51fa\u751f\u70b9\uff0c\u53ef\u4ee5\u770b\u5230\u654c\u4eba\u51fa\u751f\u540e\u56fa\u5b9a\u5411\u5317\u79fb\u52a8<\/p>\n<p><a href=\"https:\/\/yanjingang.com\/blog\/wp-content\/uploads\/2021\/08\/moving-forward.png\"><img loading=\"lazy\" class=\"alignnone size-full wp-image-5131\" src=\"https:\/\/yanjingang.com\/blog\/wp-content\/uploads\/2021\/08\/moving-forward.png\" alt=\"\" width=\"460\" height=\"460\" srcset=\"https:\/\/yanjingang.com\/blog\/wp-content\/uploads\/2021\/08\/moving-forward.png 460w, https:\/\/yanjingang.com\/blog\/wp-content\/uploads\/2021\/08\/moving-forward-150x150.png 150w, https:\/\/yanjingang.com\/blog\/wp-content\/uploads\/2021\/08\/moving-forward-300x300.png 300w\" sizes=\"(max-width: 460px) 100vw, 460px\" \/><\/a><\/p>\n<h3>3.2.\u8ddf\u968f\u8def\u5f84\uff08Following the Path\uff09<\/h3>\n<p>\u6211\u4eec\u7684\u654c\u4eba\u6b63\u5728\u524d\u8fdb\uff0c\u4f46\u4ed6\u4eec\u8fd8\u6ca1\u6709\u6cbf\u7740\u8def\u5f84\u524d\u884c\u3002\u4e3a\u4e86\u5b9e\u73b0\u8fd9\u4e00\u76ee\u6807\uff0c\u654c\u4eba\u5fc5\u987b\u77e5\u9053\u4e0b\u4e00\u6b65\u8981\u53bb\u54ea\u91cc\u3002<\/p>\n<p>a.\u7ed9GameTile\u4e00\u4e2a\u516c\u5171getter\u5c5e\u6027\u6765\u68c0\u7d22\u8def\u5f84\u4e0a\u7684\u4e0b\u4e00\u4e2a\u74e6\u7247<\/p>\n<pre class=\"pure-highlightjs\"><code class=\"\">\/\/ \u74e6\u7247\u5bf9\u8c61\r\npublic class GameTile : MonoBehaviour{\r\n    ...\r\n    public GameTile NextTileOnPath =&gt; nextOnPath; \/\/\u8def\u5f84\u4e0b\u4e00\u74e6\u7247\u5c5e\u6027\u5141\u8bb8\u516c\u5f00\u8bbf\u95ee\r\n    ...\r\n}\r\n<\/code><\/pre>\n<p>b.\u8ba9\u654c\u4eba\u8ffd\u8e2a\u4e24\u4e2a\u74e6\u7247\uff0c\u8fd9\u6837\u5b83\u5c31\u4e0d\u4f1a\u53d7\u5230\u8def\u5f84\u53d8\u5316\u7684\u5f71\u54cd\u3002\u8fd8\u8981\u8ffd\u8e2a\u4f4d\u7f6e\uff0c\u8fd9\u6837\u6211\u4eec\u5c31\u4e0d\u5fc5\u5728\u6bcf\u4e00\u5e27\u4e2d\u68c0\u7d22\u5b83\u4eec\u3002\u5b83\u4e5f\u9700\u8981\u8ffd\u8e2a\u8fdb\u5ea6\u3002<\/p>\n<pre class=\"pure-highlightjs\"><code class=\"\">using System.Collections;\r\nusing System.Collections.Generic;\r\nusing UnityEngine;\r\n\r\n\/\/ \u654c\u4eba\r\npublic class Enemy : MonoBehaviour{\r\n    ...\r\n    \/\/ \u4f4d\u7f6e\u8ffd\u8e2a\r\n    GameTile fromTile, toTile;    \/\/\u74e6\u7247\u4ece\u54ea\u91cc\u6765\uff0c\u4e0b\u4e00\u6b65\u8981\u53bb\u54ea\u91cc\r\n    Vector3 fromPos, toPos;       \/\/\u540c\u4e0a\u7684\u5750\u6807\r\n    float progress;               \/\/\u8fdb\u5ea6\r\n\r\n\r\n    \/\/ \u8bbe\u7f6e\u654c\u4eba\u5230\u51fa\u751f\u74e6\u7247\u4f4d\u7f6e\r\n    public void SpawnOn(GameTile tile){\r\n      \/\/transform.localPosition = tile.transform.localPosition;\r\n      Debug.Assert(tile.NextTileOnPath != null, \"Nowhere to go!\", this);\r\n      \/\/ \u4f4d\u7f6e\u8ddf\u8e2a\u5c5e\u6027\u8bbe\u7f6e\r\n      fromTile = tile;\r\n      fromPos = fromTile.transform.localPosition;\r\n      toTile = tile.NextTileOnPath;\r\n      toPos = toTile.transform.localPosition;\r\n      progress = 0f;\r\n    }\r\n\r\n    \/\/ \u66f4\u65b0\u654c\u4eba\u4f4d\u7f6e\u7b49\u4fe1\u606f\uff08\u654c\u4eba\u6b7b\u4ea1\u65f6\u8fd4\u56defalse\uff09\r\n    public bool GameUpdate(){\r\n      \/\/transform.localPosition += Vector3.forward * Time.deltaTime;  \/\/\u6839\u636e\u65f6\u95f4\u5411\u524d\u79fb\u52a8\r\n      progress += Time.deltaTime;\r\n      while(progress &gt;= 1f){\r\n        if(toTile.NextTileOnPath == null){  \/\/\u6ca1\u6709\u4e0b\u4e00\u74e6\u7247\uff0c\u9500\u6bc1\u654c\u4eba\r\n          OriginFactory.Reclaim(this);\r\n          return false;\r\n        }\r\n        \/\/ \u66f4\u65b0\u4f4d\u7f6e\u8ddf\u8e2a\u5173\u7cfb\r\n        fromTile = toTile;\r\n        fromPos = toPos;\r\n        toTile = toTile.NextTileOnPath;\r\n        toPos = toTile.transform.localPosition;\r\n        progress -= 1f;\r\n      }\r\n      transform.localPosition = Vector3.LerpUnclamped(fromPos, toPos, progress);  \/\/\u4e3a\u4e86\u786e\u4fdd\u79fb\u52a8\u7684\u5e73\u987a\u6027\uff0c\u6211\u4eec\u4f7f\u7528\u8fdb\u5ea6\u63d2\u503c\r\n      return true;\r\n    }\r\n}\r\n<\/code><\/pre>\n<p>c.\u8fd0\u884c\u540e\u53ef\u4ee5\u770b\u5230\u654c\u4eba\u4f1a\u6cbf\u7740\u89c4\u5212\u8def\u5f84\u5e73\u6ed1\u79fb\u52a8\uff0c\u5f53\u7136\u4f60\u4e5f\u8bd5\u4e0b\u6dfb\u52a0\u591a\u4e2a\u654c\u4eba\u51fa\u751f\u70b9\u3001\u76ee\u7684\u5730\uff0c\u5e76\u7528\u5899\u963b\u6321\u4ed6\u4eec<\/p>\n<p><a href=\"https:\/\/yanjingang.com\/blog\/wp-content\/uploads\/2021\/08\/enemy-move.gif\"><img loading=\"lazy\" class=\"alignnone wp-image-5133\" src=\"https:\/\/yanjingang.com\/blog\/wp-content\/uploads\/2021\/08\/enemy-move.gif\" alt=\"\" width=\"328\" height=\"328\" \/><\/a><\/p>\n<h3>3.3.\u8fb9\u5230\u8fb9\u5e73\u6ed1\u8f6c\u5f2f\uff08Going From Edge to Edge\uff09<\/h3>\n<p>\u4ed4\u7ec6\u89c2\u5bdf\uff0c\u4f60\u4f1a\u53d1\u73b0\u76ee\u524d\u654c\u4eba\u5728\u8f6c\u5411\u65f6\uff0c\u4f1a\u5148\u79fb\u52a8\u5230\u74e6\u7247\u4e2d\u5fc3\u540e\u7a81\u7136\u76f4\u89d2\u6539\u53d8\u65b9\u5411\u3002\u5982\u679c\u654c\u4eba\u53ef\u4ee5\u5728\u74e6\u7247\u95f4\u76f4\u63a5\u659c\u7740\u6216\u7ed5\u5f27\u5f62\u79fb\u52a8\u4f1a\u66f4\u6d41\u7545\u4e9b\u3002\u6211\u4eec\u53ef\u4ee5\u901a\u8fc7\u5e73\u5747\u76f8\u90bb\u74e6\u7247\u7684\u4f4d\u7f6e\u6765\u627e\u5230\u5b83\u4eec\u4e4b\u95f4\u7684\u8fb9\u7f18\u70b9\u6765\u5b9e\u73b0\u3002<\/p>\n<p>a.\u901a\u8fc7ExitPoint\u5c5e\u6027\u8bb0\u5f55\u4e0b\u4e00\u8def\u5f84\u76f8\u90bb\u74e6\u7247\u8fb9\u7f18\u70b9<\/p>\n<pre class=\"pure-highlightjs\"><code class=\"\">\/\/ \u74e6\u7247\u5bf9\u8c61\r\npublic class GameTile : MonoBehaviour\r\n{\r\n    ...\r\n    public Vector3 ExitPoint { get; private set;} \/\/\u6839\u636e\u4e0b\u4e00\u74e6\u7247\u7684\u4f4d\u7f6e\uff0c\u786e\u5b9a\u654c\u4eba\u79bb\u5f00\u5f53\u524d\u74e6\u7247\u65f6\u7684\u79bb\u5f00\u70b9\r\n    ...\r\n    \/\/ \u8bbe\u7f6e\u4e3a\u76ee\u7684\u5730\r\n    public void BecameDestination(){\r\n      ...\r\n      ExitPoint = transform.localPosition;  \/\/\u76ee\u7684\u5730\u7684\u51fa\u53e3\u70b9\u5728\u5176\u4e2d\u5fc3\u70b9\r\n    }\r\n\r\n    \/\/ \u53cd\u5411\u8bbe\u7f6e\u8def\u5f84\u5173\u7cfb(\u4ece\u76ee\u7684\u5730\u74e6\u7247\u5f00\u59cb)\r\n    GameTile GrowPathTo(GameTile neighbor){\r\n      ...\r\n      neighbor.ExitPoint = (neighbor.transform.localPosition + transform.localPosition) * 0.5f; \/\/\u5e73\u5747\u76f8\u90bb\u74e6\u7247\u7684\u4f4d\u7f6e\u6765\u627e\u5230\u5b83\u4eec\u4e4b\u95f4\u7684\u8fb9\u7f18\u70b9\r\n      return neighbor.Content.Type != GameTileContentType.Wall ? neighbor : null; \r\n    }\r\n    ...\r\n}\r\n<\/code><\/pre>\n<p>b.\u8c03\u6574\u654c\u4eba\u7684\u884c\u8d70\u76ee\u7684\u5730\u4e3a\u74e6\u7247\u8fb9\u7f18\u70b9\u4ea4\u63a5<\/p>\n<pre class=\"pure-highlightjs\"><code class=\"\">\/\/ \u654c\u4eba\r\npublic class Enemy : MonoBehaviour\r\n{\r\n    ...\r\n    \/\/ \u8bbe\u7f6e\u654c\u4eba\u5230\u51fa\u751f\u74e6\u7247\u4f4d\u7f6e\r\n    public void SpawnOn(GameTile tile){\r\n      ...\r\n      toPos =  fromTile.ExitPoint;  \/\/\u5f53\u524d\u74e6\u7247\u4e0e\u4e0b\u4e00\u74e6\u7247\u7684\u4f4d\u7f6e\u8fb9\u7f18\u70b9\u4e2d\u5fc3\u70b9 \/\/toTile.transform.localPosition;\r\n      progress = 0f;\r\n    }\r\n\r\n    \/\/ \u66f4\u65b0\u654c\u4eba\u4f4d\u7f6e\u7b49\u4fe1\u606f\uff08\u654c\u4eba\u6b7b\u4ea1\u65f6\u8fd4\u56defalse\uff09\r\n    public bool GameUpdate(){\r\n      ...\r\n        toPos = fromTile.ExitPoint; \/\/\u5f53\u524d\u74e6\u7247\u4e0e\u4e0b\u4e00\u74e6\u7247\u7684\u4f4d\u7f6e\u8fb9\u7f18\u70b9\u4e2d\u5fc3\u70b9  \/\/toTile.transform.localPosition;\r\n        progress -= 1f;\r\n      }\r\n      ...\r\n    }\r\n}\r\n<\/code><\/pre>\n<p>c.\u8fd0\u884c\u540e\u53ef\u4ee5\u770b\u5230\uff0c\u654c\u4eba\u5728\u8f6c\u5f2f\u65f6\u5df2\u7ecf\u53d8\u5f97\u5e73\u6ed1<\/p>\n<p><a href=\"https:\/\/yanjingang.com\/blog\/wp-content\/uploads\/2021\/08\/enemies-move.png\"><img loading=\"lazy\" class=\"alignnone wp-image-5138 size-medium\" src=\"https:\/\/yanjingang.com\/blog\/wp-content\/uploads\/2021\/08\/enemies-move-300x298.png\" alt=\"\" width=\"300\" height=\"298\" srcset=\"https:\/\/yanjingang.com\/blog\/wp-content\/uploads\/2021\/08\/enemies-move-300x298.png 300w, https:\/\/yanjingang.com\/blog\/wp-content\/uploads\/2021\/08\/enemies-move-150x150.png 150w, https:\/\/yanjingang.com\/blog\/wp-content\/uploads\/2021\/08\/enemies-move-624x619.png 624w, https:\/\/yanjingang.com\/blog\/wp-content\/uploads\/2021\/08\/enemies-move.png 766w\" sizes=\"(max-width: 300px) 100vw, 300px\" \/><\/a><\/p>\n<h3>3.4.\u8f6c\u5411\uff08Orientation\uff09<\/h3>\n<p><a href=\"https:\/\/yanjingang.com\/blog\/wp-content\/uploads\/2021\/08\/move-ph.gif\"><img loading=\"lazy\" class=\"alignnone wp-image-5136\" src=\"https:\/\/yanjingang.com\/blog\/wp-content\/uploads\/2021\/08\/move-ph.gif\" alt=\"\" width=\"333\" height=\"333\" \/><\/a><\/p>\n<p>\u76ee\u524d\u654c\u4eba\u80fd\u6cbf\u7740\u9053\u8def\u524d\u8fdb\uff0c\u4f46\u4ece\u672a\u6539\u53d8\u8fc7\u671d\u5411\u3002\u4ece\u4e0a\u56fe\u53ef\u4ee5\u770b\u5230\u5f53\u654c\u4eba\u7531\u4e8e\u8def\u5f84\u53d8\u5316\u800c\u8f6c\u8eab\u65f6\uff0c\u5b83\u4eec\u4f1a\u4fdd\u6301\u9759\u6b62\u4e00\u79d2\u949f\u540e\u76f4\u63a5\u5012\u9000\u3002\u5047\u5982\u6211\u4eec\u628a\u65b9\u5757\u6539\u4e3a\u602a\u517d\u7684\u8bdd\uff0c\u4f60\u4e00\u5b9a\u4f1a\u770b\u5230\u602a\u517d\u5c41\u80a1\u671d\u524d\uff0c\u5012\u7740\u8d70\u8def\u3002\u6211\u4eec\u9700\u8981\u589e\u52a0\u4fdd\u6301\u4e00\u9762\u671d\u524d\u7684\u7684\u673a\u5236\uff0c\u5c31\u5fc5\u987b\u77e5\u9053\u654c\u4eba\u6cbf\u8def\u5f84\u524d\u8fdb\u7684\u65b9\u5411\uff0c\u6211\u4eec\u5728\u654c\u4eba\u4e2d\u5b9e\u73b0\u671d\u5411\u7684\u5c5e\u6027\u548c\u8f6c\u5411\u7684\u673a\u5236\u3002<\/p>\n<p>a.\u6dfb\u52a0\u671d\u5411\u679a\u4e3e\u548c\u65b9\u5411\u65cb\u8f6c\u89d2\u5ea6\u7b49\u6269\u5c55\u65b9\u6cd5<\/p>\n<pre class=\"pure-highlightjs\"><code class=\"\">using UnityEngine;\r\n\r\n\/\/ \u671d\u5411\r\npublic enum Direction{  \/\/\u65b9\u5411\u679a\u4e3e\r\n  North, East, South, West\r\n}\r\n\r\n\/\/ \u8f6c\u5411\u52a8\u4f5c\r\npublic enum DirectionChange{  \/\/\u8f6c\u5411\u52a8\u4f5c\u679a\u4e3e\r\n  None, TurnRight, TurnLeft, TurnAround\r\n}\r\n\r\n\/\/ \u65b9\u5411\u7c7b\u65b9\u6cd5\u6269\u5c55\u7c7b\uff08\u7c7b\u5185\u90e8\u7684static\u65b9\u6cd5\u53ef\u81ea\u52a8\u6269\u5c55\u5230\u4efb\u4f55\u5bf9\u8c61\u4e0a\u4f7f\u7528\uff09\r\npublic static class DirectionExtensions{\r\n    \/\/ \u65cb\u8f6c\u89d2\u5ea6\r\n    static Quaternion[] rotations = {\r\n      Quaternion.identity,\r\n      Quaternion.Euler(0f, 90f, 0f),\r\n      Quaternion.Euler(0f, 180f, 0f),\r\n      Quaternion.Euler(0f, 270f, 0f)\r\n    };\r\n\r\n    \/\/ \u83b7\u53d6\u6307\u5b9a\u65b9\u5411\u7684\u65cb\u8f6c\u89d2\u5ea6\r\n    public static Quaternion GetRotation(this Direction direction){\r\n      return rotations[(int)direction];\r\n    }\r\n\r\n    \/\/ \u83b7\u53d6\u4ece\u4e00\u4e2a\u65b9\u5411\u5230\u4e0b\u4e00\u4e2a\u65b9\u5411\u7684\u8f6c\u5411\u52a8\u4f5c\r\n    public static DirectionChange GetDirectionChangeTo(this Direction current, Direction next){\r\n      if(current == next){  \/\/\u4e0d\u8f6c\r\n        return DirectionChange.None;\r\n      }else if(current + 1 == next || current - 3 == next){  \/\/\u53f3\u8f6c\r\n        return DirectionChange.TurnRight;\r\n      }else if(current - 1 == next || current + 3 == next){  \/\/\u5de6\u8f6c\r\n        return DirectionChange.TurnLeft;\r\n      }\r\n      return DirectionChange.TurnAround;  \/\/\u6389\u5934\r\n    }\r\n\r\n    \/\/ \u83b7\u53d6\u65b9\u5411\u89d2\u5ea6\u6570\r\n    public static float GetAngle(this Direction direction){\r\n      return (float)direction * 90f;\r\n    }\r\n}<\/code><\/pre>\n<p>b.\u74e6\u7247\u589e\u52a0\u8def\u5f84\u671d\u5411\u5c5e\u6027<\/p>\n<pre class=\"pure-highlightjs\"><code class=\"\">\/\/ \u74e6\u7247\u5bf9\u8c61\r\npublic class GameTile : MonoBehaviour\r\n{\r\n    ...\r\n    \/\/ \u8def\u5f84\u671d\u5411\r\n    public Direction PathDirection { get; private set;} \/\/\u4e0b\u4e00\u74e6\u7247\u7684\u65b9\u5411\r\n    ...\r\n    \/\/ \u53cd\u5411\u8bbe\u7f6e\u8def\u5f84\u5173\u7cfb(\u4ece\u76ee\u7684\u5730\u74e6\u7247\u5f00\u59cb)\r\n    GameTile GrowPathTo(GameTile neighbor, Direction direction){\r\n      ...\r\n      neighbor.PathDirection = direction;   \/\/\u8def\u5f84\u671d\u5411\r\n      return neighbor.Content.Type != GameTileContentType.Wall ? neighbor : null; \r\n    }\r\n    \/\/ \u6cbf\u7279\u5b9a\u65b9\u5411\u6269\u5c55\u8def\u5f84\r\n    public GameTile GrowPathNorth() =&gt; GrowPathTo(north, Direction.South); \/\/\u5411\u540e\u751f\u957f\u65f6\uff0c\u884c\u8d70\u65b9\u5411\u521a\u597d\u76f8\u53cd\r\n    public GameTile GrowPathEast() =&gt; GrowPathTo(east, Direction.West);\r\n    public GameTile GrowPathSouth() =&gt; GrowPathTo(south, Direction.North);\r\n    public GameTile GrowPathWest() =&gt; GrowPathTo(west, Direction.East);\r\n    ...\r\n}\r\n<\/code><\/pre>\n<p>c.\u654c\u4eba\u51fa\u751f\u548c\u66f4\u65b0\u65f6\u65cb\u8f6c\u654c\u4eba\u671d\u5411\u89d2\u5ea6<\/p>\n<pre class=\"pure-highlightjs\"><code class=\"\">using System.Collections;\r\nusing System.Collections.Generic;\r\nusing UnityEngine;\r\n\r\n\/\/ \u654c\u4eba\r\npublic class Enemy : MonoBehaviour\r\n{\r\n    \/\/ \u5de5\u5382\u53cd\u5411\u5f15\u7528\r\n    EnemyFactory originFactory;\r\n    public EnemyFactory OriginFactory{\r\n      get =&gt; originFactory;\r\n      set {\r\n        Debug.Assert(originFactory == null, \"Redefined origin factory!\");\r\n        originFactory = value;\r\n      }\r\n    }\r\n    \/\/ \u4f4d\u7f6e\u8ffd\u8e2a\r\n    GameTile tileFrom, tileTo;    \/\/\u74e6\u7247\u4ece\u54ea\u91cc\u6765\uff0c\u4e0b\u4e00\u6b65\u8981\u53bb\u54ea\u91cc\r\n    Vector3 positionFrom, positionTo;       \/\/\u540c\u4e0a\u7684\u5750\u6807\r\n    float progress;               \/\/\u8fdb\u5ea6\r\n    \/\/ \u65b9\u5411\u8ddf\u8e2a\r\n    Direction direction;          \/\/\u65b9\u5411\r\n    DirectionChange directionChange;  \/\/\u8f6c\u5411\u52a8\u4f5c\r\n    float directionAngleFrom, directionAngleTo; \/\/\u65b9\u5411\u5ea6\u6570\r\n\r\n\r\n    \/\/ \u8bbe\u7f6e\u654c\u4eba\u5230\u51fa\u751f\u74e6\u7247\u4f4d\u7f6e\r\n    public void SpawnOn(GameTile tile){\r\n      \/\/transform.localPosition = tile.transform.localPosition;\r\n      Debug.Assert(tile.NextTileOnPath != null, \"Nowhere to go!\", this);\r\n      \/\/ \u4f4d\u7f6e\u8ddf\u8e2a\u5c5e\u6027\u8bbe\u7f6e\r\n      tileFrom = tile;\r\n      tileTo = tile.NextTileOnPath;\r\n      progress = 0f;\r\n      \/\/ \u521d\u59cb\u5316\u654c\u4eba\u51c6\u5907\u72b6\u6001\r\n      PrepareIntro();\r\n    }\r\n\r\n    \/\/ \u654c\u4eba\u51c6\u5907\u72b6\u6001\r\n    void PrepareIntro(){\r\n      positionFrom = tileFrom.transform.localPosition;\r\n      positionTo =  tileFrom.ExitPoint;  \/\/\u5f53\u524d\u74e6\u7247\u4e0e\u4e0b\u4e00\u74e6\u7247\u7684\u4f4d\u7f6e\u8fb9\u7f18\u70b9\u4e2d\u5fc3\u70b9 \/\/tileTo.transform.localPosition;\r\n      direction = tileFrom.PathDirection; \/\/\u8def\u5f84\u65b9\u5411\r\n      directionChange = DirectionChange.None; \/\/\u521d\u59cb\u72b6\u6001\u4e0b\uff0c\u654c\u4eba\u4f1a\u4ece\u8d77\u59cb\u74e6\u7247\u7684\u4e2d\u5fc3\u79fb\u52a8\u5230\u5176\u8fb9\u7f18\uff0c\u56e0\u6b64\u4e0d\u4f1a\u53d1\u751f\u65b9\u5411\u53d8\u5316\r\n      directionAngleFrom = directionAngleTo = direction.GetAngle(); \/\/\u83b7\u53d6\u65b9\u5411\u89d2\u5ea6\u6570\r\n      transform.localRotation =  direction.GetRotation();  \/\/\u65cb\u8f6c\u654c\u4eba\u7684\u671d\u5411\u65cb\u8f6c\u89d2\u5ea6\r\n    }\r\n\r\n    \/\/ \u66f4\u65b0\u654c\u4eba\u4f4d\u7f6e\u7b49\u4fe1\u606f\uff08\u654c\u4eba\u6b7b\u4ea1\u65f6\u8fd4\u56defalse\uff09\r\n    public bool GameUpdate(){\r\n      \/\/transform.localPosition += Vector3.forward * Time.deltaTime;  \/\/\u6839\u636e\u65f6\u95f4\u5411\u524d\u79fb\u52a8\r\n      progress += Time.deltaTime;\r\n      while(progress &gt;= 1f){\r\n        if(tileTo.NextTileOnPath == null){  \/\/\u6ca1\u6709\u4e0b\u4e00\u74e6\u7247\uff0c\u9500\u6bc1\u654c\u4eba\r\n          OriginFactory.Reclaim(this);\r\n          return false;\r\n        }\r\n        \/\/ \u66f4\u65b0\u4f4d\u7f6e\u8ddf\u8e2a\u5173\u7cfb\r\n        tileFrom = tileTo;\r\n        tileTo = tileTo.NextTileOnPath;\r\n        progress -= 1f;\r\n        \/\/ \u66f4\u65b0\u654c\u4eba\u4f4d\u7f6e\u3001\u65b9\u5411\u7b49\r\n        PrepareNextState();\r\n      }\r\n      transform.localPosition = Vector3.LerpUnclamped(positionFrom, positionTo, progress);  \/\/\u4e3a\u4e86\u786e\u4fdd\u79fb\u52a8\u7684\u5e73\u987a\u6027\uff0c\u6211\u4eec\u4f7f\u7528\u8fdb\u5ea6\u63d2\u503c\r\n      \/\/ \u4f7f\u7528\u8fdb\u5ea6\u5dee\u503c\u5b9e\u73b0\u5e73\u6ed1\u8f6c\u5411\r\n      if(directionChange != DirectionChange.None){\r\n        float angle = Mathf.LerpUnclamped(directionAngleFrom, directionAngleTo, progress);\r\n        transform.localRotation = Quaternion.Euler(0f, angle, 0f);\r\n      }\r\n      return true;\r\n    }\r\n\r\n    \/\/ \u654c\u4eba\u884c\u8d70\u4e2d\uff0c\u66f4\u65b0\u654c\u4eba\u4e0b\u4e00\u6862\u4f4d\u7f6e\u3001\u65b9\u5411\u7b49\r\n    void PrepareNextState(){\r\n      positionFrom = positionTo;\r\n      positionTo = tileFrom.ExitPoint; \/\/\u5f53\u524d\u74e6\u7247\u4e0e\u4e0b\u4e00\u74e6\u7247\u7684\u4f4d\u7f6e\u8fb9\u7f18\u70b9\u4e2d\u5fc3\u70b9  \/\/tileTo.transform.localPosition;\r\n      directionChange = direction.GetDirectionChangeTo(tileFrom.PathDirection);\r\n      direction = tileFrom.PathDirection; \/\/\u8def\u5f84\u65b9\u5411\r\n      directionAngleFrom = directionAngleTo;   \/\/\u65b9\u5411\u5ea6\u6570\r\n      \/\/\u65cb\u8f6c\u89d2\u5ea6\r\n      switch(directionChange){\r\n        case DirectionChange.None: PrepareForward(); break;\r\n        case DirectionChange.TurnRight: PrepareTurnRight(); break;\r\n        case DirectionChange.TurnLeft: PrepareTurnLeft(); break;\r\n        default: PrepareTurnAround(); break;\r\n      }\r\n    }\r\n\r\n    \/\/ \u8c03\u6574\u65cb\u8f6c\u89d2\u5ea6\r\n    void PrepareForward(){\r\n      transform.localRotation =  direction.GetRotation();  \/\/\u65cb\u8f6c\u654c\u4eba\u7684\u671d\u5411\u65cb\u8f6c\u89d2\u5ea6\r\n      directionAngleTo = direction.GetAngle();   \/\/\u65b9\u5411\u5ea6\u6570\r\n    }\r\n    \/\/ \u4e07\u4e00\u8f6c\u5f2f\uff0c\u6211\u4eec\u4e0d\u4f1a\u7acb\u5373\u65cb\u8f6c\u3002 \u76f8\u53cd\uff0c\u5fc5\u987b\u63d2\u503c\u5230\u53e6\u4e00\u4e2a\u89d2\u5ea6\uff1a\u5411\u53f3\u8f6c90\u00b0\uff0c\u5411\u5de6\u8f6c90\u00b0\uff0c\u8f6c\u5f2f\u65f6\u591a180\u00b0\u3002 To\u89d2\u5ea6\u5fc5\u987b\u76f8\u5bf9\u4e8e\u5f53\u524d\u65b9\u5411\uff0c\u4ee5\u9632\u6b62\u7531\u4e8e\u7f20\u7ed5\u89d2\u5ea6\u800c\u4ee5\u9519\u8bef\u7684\u65b9\u5f0f\u65cb\u8f6c\u3002 \u6211\u4eec\u4e0d\u5fc5\u62c5\u5fc3\u4f1a\u4f4e\u4e8e0\u00b0\u6216\u9ad8\u4e8e360\u00b0\uff0c\u56e0\u4e3a\u56db\u5143\u6570\u3002Euler\u53ef\u4ee5\u89e3\u51b3\u8fd9\u4e2a\u95ee\u9898\u3002\r\n    void PrepareTurnRight(){\r\n      directionAngleTo = directionAngleFrom + 90f;   \/\/\u65b9\u5411\u5ea6\u6570\r\n    }\r\n    void PrepareTurnLeft(){\r\n      directionAngleTo = directionAngleFrom - 90f;   \/\/\u65b9\u5411\u5ea6\u6570\r\n    }\r\n    void PrepareTurnAround(){\r\n      directionAngleTo = directionAngleFrom + 180f;   \/\/\u65b9\u5411\u5ea6\u6570\r\n    }\r\n}\r\n<\/code><\/pre>\n<p>d.\u8fd0\u884c\u53ef\u4ee5\u770b\u5230\u5df2\u7ecf\u53ef\u4ee5\u770b\u5230\u65cb\u8f6c\u6389\u5934\u6548\u679c\u3002\u4e0d\u8fc7\u5982\u679c\u9047\u5230\u9891\u7e41\u5de6\u53f3\u8f6c\u5411\u65f6\uff0c\u4f1a\u6709\u70b9\u6447\u5934\u6446\u5c3e&#8230;<\/p>\n<p><a href=\"https:\/\/yanjingang.com\/blog\/wp-content\/uploads\/2021\/08\/enemies-around.gif\"><img loading=\"lazy\" class=\"alignnone wp-image-5140\" src=\"https:\/\/yanjingang.com\/blog\/wp-content\/uploads\/2021\/08\/enemies-around.gif\" alt=\"\" width=\"337\" height=\"337\" \/><\/a><\/p>\n<h2>4.\u53ef\u53d8\u654c\u4eba\uff08Enemy Variety\uff09<\/h2>\n<p>\u6211\u4eec\u6709\u4e00\u7fa4\u654c\u4eba\uff0c\u5b83\u4eec\u90fd\u662f\u76f8\u540c\u7684\u7acb\u65b9\u4f53\uff0c\u4ee5\u76f8\u540c\u7684\u901f\u5ea6\u79fb\u52a8\u3002 \u7ed3\u679c\u53ef\u80fd\u770b\u8d77\u6765\u50cf\u662f\u4e00\u6761\u957f\u957f\u7684\u86c7\uff0c\u800c\u4e0d\u662f\u5355\u4e2a\u654c\u4eba\u3002 \u8ba9\u6211\u4eec\u901a\u8fc7\u968f\u673a\u5316\u5b83\u4eec\u7684\u5927\u5c0f\uff0c\u504f\u79fb\u91cf\u548c\u901f\u5ea6\u4f7f\u5b83\u4eec\u66f4\u52a0\u72ec\u7279\u3002<\/p>\n<h3>4.1.\u53ef\u53d8\u968f\u673a\uff08Float Range\uff09<\/h3>\n<p>\u968f\u673a\u7c7b\u51c6\u5907\u3002<\/p>\n<pre class=\"pure-highlightjs\"><code class=\"\">using System.Collections;\r\nusing System.Collections.Generic;\r\nusing UnityEngine;\r\n\r\n\/\/ \u968f\u673a\u7c7b\r\n[System.Serializable]\r\npublic struct FloatRange {\r\n\r\n\t[SerializeField]\r\n\tfloat min, max;\r\n\r\n\tpublic float Min =&gt; min;\r\n\tpublic float Max =&gt; max;\r\n\r\n\tpublic float RandomValueInRange {\r\n\t\tget {\r\n\t\t\treturn Random.Range(min, max);\r\n\t\t}\r\n\t}\r\n\r\n\tpublic FloatRange(float value) {\r\n\t\tmin = max = value;\r\n\t}\r\n\r\n\tpublic FloatRange (float min, float max) {\r\n\t\tthis.min = min;\r\n\t\tthis.max = max &lt; min ? min : max;\r\n\t}\r\n}\r\n\r\n\r\npublic class FloatRangeSliderAttribute : PropertyAttribute {\r\n\tpublic float Min { get; private set; }\r\n\tpublic float Max { get; private set; }\r\n\r\n\tpublic FloatRangeSliderAttribute (float min, float max) {\r\n\t\tMin = min;\r\n\t\tMax = max &lt; min ? min : max;\r\n\t}\r\n}\r\n<\/code><\/pre>\n<pre class=\"pure-highlightjs\"><code class=\"\">using UnityEditor;\r\nusing UnityEngine;\r\n\r\n\/\/ \u5757\u53ef\u89c6\u5316\r\n[CustomPropertyDrawer(typeof(FloatRangeSliderAttribute))]\r\npublic class FloatRangeSliderDrawer : PropertyDrawer {\r\n\r\n\tpublic override void OnGUI (\r\n\t\tRect position, SerializedProperty property, GUIContent label\r\n\t) {\r\n\t\tint originalIndentLevel = EditorGUI.indentLevel;\r\n\t\tEditorGUI.BeginProperty(position, label, property);\r\n\r\n\t\tposition = EditorGUI.PrefixLabel(\r\n\t\t\tposition, GUIUtility.GetControlID(FocusType.Passive), label\r\n\t\t);\r\n\t\tEditorGUI.indentLevel = 0;\r\n\t\tSerializedProperty minProperty = property.FindPropertyRelative(\"min\");\r\n\t\tSerializedProperty maxProperty = property.FindPropertyRelative(\"max\");\r\n\t\tfloat minValue = minProperty.floatValue;\r\n\t\tfloat maxValue = maxProperty.floatValue;\r\n\t\tfloat fieldWidth = position.width \/ 4f - 4f;\r\n\t\tfloat sliderWidth = position.width \/ 2f;\r\n\t\tposition.width = fieldWidth;\r\n\t\tminValue = EditorGUI.FloatField(position, minValue);\r\n\t\tposition.x += fieldWidth + 4f;\r\n\t\tposition.width = sliderWidth;\r\n\t\tFloatRangeSliderAttribute limit = attribute as FloatRangeSliderAttribute;\r\n\t\tEditorGUI.MinMaxSlider(\r\n\t\t\tposition, ref minValue, ref maxValue, limit.Min, limit.Max\r\n\t\t);\r\n\t\tposition.x += sliderWidth + 4f;\r\n\t\tposition.width = fieldWidth;\r\n\t\tmaxValue = EditorGUI.FloatField(position, maxValue);\r\n\t\tif (minValue &lt; limit.Min) {\r\n\t\t\tminValue = limit.Min;\r\n\t\t}\r\n\t\tif (maxValue &lt; minValue) {\r\n\t\t\tmaxValue = minValue;\r\n\t\t}\r\n\t\telse if (maxValue &gt; limit.Max) {\r\n\t\t\tmaxValue = limit.Max;\r\n\t\t}\r\n\t\tminProperty.floatValue = minValue;\r\n\t\tmaxProperty.floatValue = maxValue;\r\n\r\n\t\tEditorGUI.EndProperty();\r\n\t\tEditorGUI.indentLevel = originalIndentLevel;\r\n\t}\r\n}\r\n<\/code><\/pre>\n<h3>4.2.\u6a21\u578b\u7f29\u653e\uff08Model Scale\uff09<\/h3>\n<p>\u6211\u4eec\u9996\u5148\u8c03\u6574\u654c\u4eba\u7684\u7f29\u653e\u3002 \u5c06\u6bd4\u4f8b\u914d\u7f6e\u9009\u9879\u6dfb\u52a0\u5230EnemyFactory\u3002 \u6bd4\u4f8b\u8303\u56f4\u522b\u592a\u5927\uff0c\u80fd\u521b\u5efa\u654c\u4eba\u7684\u5fae\u578b\u548c\u5de8\u578b\u7248\u672c\u5373\u53ef\uff0c \u7c7b\u4f3c\u4e8e0.5\u20131.5\uff0c\u9ed8\u8ba4\u8bbe\u7f6e\u4e3a1\u3002\u5728Get\u7684\u6b64\u8303\u56f4\u5185\u9009\u62e9\u4e00\u4e2a\u968f\u673a\u6bd4\u4f8b\uff0c\u5e76\u901a\u8fc7\u65b0\u7684Initialize\u65b9\u6cd5\u5c06\u5176\u4f20\u9012\u7ed9\u654c\u4eba\u3002<\/p>\n<pre class=\"pure-highlightjs\"><code class=\"\">\/\/ \u654c\u4eba\u9884\u5236\u4ef6\u5de5\u5382\r\npublic class EnemyFactory : GameObjectFactory{\r\n    ...\r\n    \/\/ \u7f29\u653e\u6bd4\u4f8b\u8303\u56f4\r\n    [SerializeField, FloatRangeSlider(0.5f, 2f)]\r\n    FloatRange scale = new FloatRange(1f);\r\n\r\n    \/\/ \u751f\u6210\u654c\u4eba\r\n    public Enemy Get(){\r\n      ...\r\n      o.Initialize(scale.RandomValueInRange); \/\/\u968f\u673a\u5927\u5c0f\r\n      return o;\r\n    }\r\n    ...\r\n}\r\n\r\n\/\/ \u654c\u4eba\r\npublic class Enemy : MonoBehaviour{\r\n    ...\r\n    \/\/ \u521d\u59cb\u5316\r\n    public void Initialize(float scale){\r\n      model.localScale = new Vector3(scale, scale, scale);\r\n    }\r\n    ...\r\n}\r\n<\/code><\/pre>\n<p><a href=\"https:\/\/yanjingang.com\/blog\/wp-content\/uploads\/2021\/08\/scale-slider.png\"><img loading=\"lazy\" class=\"alignnone wp-image-5145 size-medium\" src=\"https:\/\/yanjingang.com\/blog\/wp-content\/uploads\/2021\/08\/scale-slider-300x56.png\" alt=\"\" width=\"300\" height=\"56\" srcset=\"https:\/\/yanjingang.com\/blog\/wp-content\/uploads\/2021\/08\/scale-slider-300x56.png 300w, https:\/\/yanjingang.com\/blog\/wp-content\/uploads\/2021\/08\/scale-slider-624x117.png 624w, https:\/\/yanjingang.com\/blog\/wp-content\/uploads\/2021\/08\/scale-slider.png 640w\" sizes=\"(max-width: 300px) 100vw, 300px\" \/><\/a><\/p>\n<p><a href=\"https:\/\/yanjingang.com\/blog\/wp-content\/uploads\/2021\/08\/enemy-size.png\"><img loading=\"lazy\" class=\"alignnone  wp-image-5153\" src=\"https:\/\/yanjingang.com\/blog\/wp-content\/uploads\/2021\/08\/enemy-size.png\" alt=\"\" width=\"459\" height=\"461\" srcset=\"https:\/\/yanjingang.com\/blog\/wp-content\/uploads\/2021\/08\/enemy-size.png 764w, https:\/\/yanjingang.com\/blog\/wp-content\/uploads\/2021\/08\/enemy-size-150x150.png 150w, https:\/\/yanjingang.com\/blog\/wp-content\/uploads\/2021\/08\/enemy-size-298x300.png 298w, https:\/\/yanjingang.com\/blog\/wp-content\/uploads\/2021\/08\/enemy-size-624x627.png 624w\" sizes=\"(max-width: 459px) 100vw, 459px\" \/><\/a><\/p>\n<p>\u4eca\u5929\u6211\u4eec\u91cd\u70b9\u5b66\u4e60\u4e86\u5982\u4f55\u5728\u5730\u56fe\u4e2d\u6dfb\u52a0\u51fa\u751f\u70b9\u3001\u654c\u4eba\uff0c\u5e76\u8ba9\u654c\u4eba\u6cbf\u7740\u89c4\u5212\u8def\u5f84\u524d\u5f80\u79bb\u4ed6\u6700\u8fd1\u7684\u76ee\u7684\u5730\uff0c\u5e76\u5b9e\u73b0\u4e86\u8f6c\u5f2f\u548c\u6389\u5934\u52a8\u4f5c\u7684\u63d2\u503c\u5e73\u6ed1\u7b49\u3002<\/p>\n<p>\u4e0b\u4e00\u6b21\uff0c\u6211\u4eec\u5c1d\u8bd5\u521b\u5efa\u9632\u5fa1\u5854\u6765\u653b\u51fb\u884c\u8d70\u7684\u654c\u4eba\uff0c\u597d\u671f\u5f85\uff5e\uff01<\/p>\n<p>yan 21.8.15<\/p>\n<p>\u53c2\u8003\uff1a<\/p>\n<p><a href=\"https:\/\/catlikecoding.com\/unity\/tutorials\/tower-defense\/enemies\/\" target=\"_blank\" rel=\"noopener noreferrer\">Unity Tower Defense \u2013 Enemies<\/a><\/p>\n<p><a href=\"https:\/\/zhuanlan.zhihu.com\/p\/317194027\" target=\"_blank\" rel=\"noopener noreferrer\">Unity Demo\u6559\u7a0b\u7cfb\u5217\u2014\u2014Unity\u5854\u9632\u6e38\u620f-\u654c\u4eba<\/a><\/p>\n","protected":false},"excerpt":{"rendered":"<p>\u4e0a\u4e00\u8282\uff0c\u6211\u4eec\u8bb2\u89e3\u4e86\u5982\u4f55\u521b\u5efa\u6e38\u620f\u5730\u56fe\u3001\u76ee\u7684\u5730\u548c\u5899\u7684\u653e\u7f6e\u3001\u5bfb\u8def\u3002\u672c\u8282\u5c06\u91cd\u70b9\u5b66\u4e60\u5982\u4f55\u5728\u5730\u56fe\u4e2d\u6dfb\u52a0\u51fa\u751f\u70b9\u3001\u654c\u4eba\uff0c\u5e76\u8ba9\u654c [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":[],"categories":[1140],"tags":[884,1141,1145,1144,1146],"_links":{"self":[{"href":"https:\/\/yanjingang.com\/blog\/index.php?rest_route=\/wp\/v2\/posts\/5103"}],"collection":[{"href":"https:\/\/yanjingang.com\/blog\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/yanjingang.com\/blog\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/yanjingang.com\/blog\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/yanjingang.com\/blog\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=5103"}],"version-history":[{"count":0,"href":"https:\/\/yanjingang.com\/blog\/index.php?rest_route=\/wp\/v2\/posts\/5103\/revisions"}],"wp:attachment":[{"href":"https:\/\/yanjingang.com\/blog\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=5103"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/yanjingang.com\/blog\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=5103"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/yanjingang.com\/blog\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=5103"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}