Unity2D游戏
一、图片
1 人物
Pixels Per Unit:每个单元的像素。值越大图显示越小。
Pivot:中心点。
Default——Max Size:一般选宽高中的大的值的接近值。例如290 * 500 选 512。
2 问题
- 图片太小:调节图片的Pixels Per Unit。
 
二、组件
1 Sprite Renderer
Sorting Layer:排序层。可以添加层,越在前面的层显示越在屏幕的前面。一般用Default设置Order in Layer既可。
Order in Layer:层。值越大显示越在屏幕前。
2 Camera
Projection:Orthographic。
size:相机中心到边缘的unit个数。
三、动画
1 人物动画
新建动画后,直接旋中所有图片拖拽激到animation中,再点击右边三个小点的Show Sample Rate(每秒帧数)进行调整既可。
2 Chinemachine
安装:
- Window——Package Manager——Packages:Unity Registry——Chinemachine——install
 
- Hierarchy右键——Chinemachine——2D Camera。(此时主摄像机由CM vcam1控制)
 
CM vcam1:
- Follow:跟随对象。
 
- Lens——Ortho Size:网格大小。
 
- Body:
- Damping:当人物离中心点 n 距离摄像机才开始移动。
 
- Screen:人物所处位置。
 
- Dead Zone:在范围中相机完全不移动。
 
- Soft Zone:蓝色区域大小。玩家可移动的区域。红色区域玩家不可到达。
 
- Bias:蓝色区域移动。
 
 
边界:
- Hierarchy右键——Create——Create Empty
 
- Add Component——Polygon Collider 2D
 
- Points——Path——Element0——Size:4
 
- 拖动边界为整个地图。
 
- CM vcam1——Extensions——Add Extension——Cinemachine Confiner
 
- 拖入空物体至Bounding Shape 2D
 
四、脚本
1 属性
 public float speed; public int enemyLife;
 
  public Vector3 targetPosition, originPosition, terminalPosition;
 
  private bool isAfterBattleCheck;
 
  private BoxCollider2D box; private Sprite private Animator animator; public GameObject attackCollider;
 
  private GameObject player;
 
  | 
 
2 移动
     void FixedUpdate()     {         Move();     }
      private void Move()     {         float leftRight = Input.GetAxisRaw("Horizontal");         float upDown = Input.GetAxisRaw("Vertical");                  if (leftRight > 0)         {             transform.localScale = new Vector3(1f, 1f, 1f);         }         else if (leftRight < 0)         {             transform.localScale = new Vector3(-1f, 1f, 1f);         }                  transform.Translate(leftRight * Time.fixedDeltaTime * speed, upDown * Time.fixedDeltaTime * speed, 0);                  animator.SetFloat("Run", Mathf.Max(Mathf.Abs(leftRight), Mathf.Abs(upDown)));     }
 
  public class PlayerControl : MonoBehaviour {     private Rigidbody2D rb;
      [SerializeField] private float moveSpeed;     [SerializeField] private float jumpForce;     private float xInput;
      void Start()     {         rb = GetComponent<Rigidbody2D>();     }          void Update()     {         Movement();         CheckInput();     }
      private void CheckInput()     {         xInput = Input.GetAxisRaw("Horizontal");
          if (Input.GetKeyDown(KeyCode.Space))         {             Jump();         }     }
      private void Movement()     {         rb.velocity = new Vector2(xInput * moveSpeed, rb.velocity.y);     }
      private void Jump()     {         rb.velocity = new Vector2(rb.velocity.x, jumpForce);     } }
 
  | 
 
3 设置人物属性
public class ShowInInspector : MonoBehaviour {     [System.Serializable]     public struct Attributes     {         public int HP;         public int MP;     }
      [System.Serializable]     public struct Character     {         public int skinID;         public int characterID;     } 	                    public Attributes attribute;
           public Character character;               [Space] }
   | 
 
五、场景
1 Tile Palette
创建 Palette:
Window——2D——Tile Palette——Create New Palette。
 
拖入图片,图片转换格式后存入一个新文件夹中。
 
Edit Palette:
- S:选中物体
 
- M:移动物体
 
- B:选取后直接移动到场景中。
 
- I:先选中,后按左键复制物体。
 
- D:擦去场景中的错误。
 
使用 Palette:
- Hierarchy右键——Create——2D Object——Tilemap
 
- 在Palette中选择Active Tilemap
 
- 在Palette中安B选择物体移动到场景中。
 
碰撞器:
- 添加Tilemap Collider 2D。(该项比较消耗资源,需要进行后续设置)
 
- 添加Composite Collider 2D,会自动添加Rigidbody 2D。
 
- Rigidbody 2D——Body Type——Static。
 
- Tilemap Collider 2D——Used By Composite。
 
2 边界
限定边界用碰撞器。当玩家碰到底部物件(通过底部物件的名字判断)时直接杀死玩家。
3 平台
可穿越平台:
- 设置一个父空物件,将地形作为该父物件的子物件。
 
- 给父物件添加Platform Effector 2D和Box Collider,Box Collider选择Used By Effector
 
- 设置Box Collider 2D的大小,使其大小仅仅为地面区域。
 
Platform Effector 2D——Surface Arc:作用弧度。
 public class PlatformMove : MonoBehaviour {     public Vector3 originPosition, targetPosition, terminalPosition;     public float moveSpeed;
      private void Start()     {         targetPosition = terminalPosition;         originPosition = transform.position;     }
           private void Update()     {         Move();     }
      private void Move()     {         if (transform.position == targetPosition)         {             if (targetPosition == originPosition)             {                 targetPosition = terminalPosition;             }             else             {                 targetPosition = originPosition;             }         }         transform.position = Vector3.MoveTowards(transform.position, targetPosition, moveSpeed * Time.deltaTime);     } }
 
 
  private void OnTriggerEnter2D(Collider2D collision) {     if(collision.tag == "AirPlatform")     {                  playerScript.transform.parent = collision.transform;     } } private void OnTriggerExit2D(Collider2D collision) {     if(collision.tag == "AirPlatform")     { 		playerScript.transform.parent = null;     } }
 
  | 
 
4 UI
- Canvas——Render Mode——Screen Space-Camera
 
- 拖入主摄像机
 
- Order in Layer设置大于场景Order in Layer
 
Canvas Renderer 的 “Cull Transparent Mesh” 属性可以根据 UI 元素的透明度进行渲染剔除,以提高渲染性能。需要注意的是,这个属性只会影响透明的部分,对不透明的像素没有影响。
六、设计
1 路线设计
根路线——多条子路线——多个路线点。
选择时,从跟路线出发,选择多条子路线中的一条以此保证随机性。
这些对象都是空物体。
七、打包
手机型号查询:
Package Manager——Device Simulator
 
Simulator——将game改成Simulator
 
- 查看手机的Safe Area
 
背景有留白:
多创建两张背景,将背景进行拼接,从而解决留白。
 
  public class SafeAreaPanel : MonoBehaviour {     private RectTransform panel;
      void Start()     {         panel = GetComponent<RectTransform>();
          Vector2 safeAreaMinPosition = Screen.safeArea.position;         Vector2 safeAreaMaxPosition = Screen.safeArea.position + Screen.safeArea.size;
          safeAreaMinPosition.x = safeAreaMinPosition.x / Screen.width;         safeAreaMinPosition.y = safeAreaMinPosition.y / Screen.height;
          safeAreaMaxPosition.x = safeAreaMaxPosition.x / Screen.width;         safeAreaMaxPosition.y = safeAreaMaxPosition.y / Screen.height;
          panel.anchorMin = safeAreaMinPosition;         panel.anchorMax = safeAreaMaxPosition;     } }
 
  | 
 
八、问题
1 物件翻转
刚体中设置固定Z轴。
2 墙壁
卡墙:给玩家的Rigidbody 2D加Material。Material的Friction设置为0。
靠墙跳无法触发跳跃动画:父物体下加一个空物体,空物体加上Box Collider 2D,选上Is Trigger,拉到人物底部,调整大小为宽度同人物宽,高度尽可能小。
 public class PlayerTrigger : MonoBehaviour {     private PlayerControl playerScript;     void Start()     {                  playerScript = GetComponentInParent<PlayerControl>();     }
      private void OnTriggerEnter2D(Collider2D collision)     {         if (collision.tag == "Ground")         {             playerScript.canJump = true;             playerScript.animator.SetBool("Jump", false);         }     } }
 
 
  | 
 
3 碰撞器
碰撞器位置:
碰撞器休眠:
当玩家不移动时,碰撞器会休眠。此时再攻击会无法触发攻击效果。
 
解决方法1:Rigidbody 2D——Sleeping Mode——Never Sleep(注:该设置十分消耗性能,一般只给玩家设置即可)
 
解决方法2:玩家的判断攻击的子物体中加上Rigidbody 2D——Body Type——Kinematic。此时玩家不需要再设置为Never Sleep了。因为Collider中的Info的判定信息变成了攻击的子物体而不是玩家。
 
无法重复碰撞:
摄像头碰撞器:
触发器重叠:
4 重复动画播放
设置了trigger的属性可能会重复播放动画。
解决方法:需要在动画的最后一帧加上事件,事件绑定函数重置触发器。
 private void Update() {     float swordAttack = Input.GetAxisRaw("Sword");     if (swordAttack > 0)     {         animator.SetTrigger("Attack");         isAttack = true;         canJump = false;     } }
  public void SetIsAttackFalse() {     isAttack = false;     canJump = true;     animator.ResetTrigger("Attack"); }
 
  | 
 
5 受伤
在播放受伤动画时输入的值会被记录,导致动画结束后会立刻执行响应动作。(例如:被击飞时按了闪现,落地后会立刻闪现)
受伤后不会无敌,会重复受伤。
解决方法:在相应条件后面加上&& isHurt == false。例如:if (jump > 0 && canJump == true && isHurt == false)
6 远程武器被挡
武器设定碰撞为if (collision != null && collision.tag != "Player")。当玩家使用远程武器时,武器会被敌人设定的AttackCollider空物体给摧毁。
解决方法:设定Layer,玩家攻击为PlayerAttack,敌人攻击为EnemyAttack。然后到Edit——Project Setting——Physics 2D,然后把两者的作用关系的勾取消掉即可。
7 背景
背景不随人物移动。
解决方法:拖动背景作为主摄像机的子物体。
8 寻路穿墙
某些敌方单位会跟随玩家移动,此时会发生穿墙,浮空等BUG。
解决方法:
- 在地方单位两侧设置停止点,停止点为空物体,加上触发器。
 
- 给地方单位加上刚体,设置为Body Type——Kinematic。
 
9 寻找物件
 canvas = GameObject.Find("/Canvas").GetComponent<Canvas>();
 
  | 
 
10 子弹判定
因为速度太快,用碰撞和触发不一定会触发,所以要用射线判定。
图层:设置哪一层射线就检测哪一层。可以给敌人专门做一个层。
using UnityEngine;
  public class Bullet : MonoBehaviour {     public float speed = 50f;     public float maxDistance = 300f;     public LayerMask hitLayers;  
      private void Update()     {                  float distance = speed * Time.deltaTime;
          RaycastHit hit;                  if (Physics.Raycast(transform.position, transform.forward, out hit, distance, hitLayers))         {                          Debug.Log("射中了:" + hit.collider.gameObject.name);
                       }
                   transform.Translate(Vector3.forward * distance);
                   if (Vector3.Distance(transform.parent.position, transform.position) >= maxDistance)         {             Destroy(gameObject);         }     } }
 
   | 
 
九、优化
1 资源重复加载
为节约性能,把需要重复加载的内容放在ResourcesManager类中。然后在该类中写静态构造函数和静态方法。静态构造函数负责读取资源到类中。静态函数负责被其他类调用返回指定资源。
 public class ResourcesManager : MonoBehaviour {     private static Dictionary<int, Sprite> spriteDict;          static ResourcesManager()     {         spriteDict = new Dictionary<int, Sprite>();         SpriteAtlas sprites = Resources.Load<SpriteAtlas>("People");         foreach (Sprite sprite in sprites.GetPackables())         {             int id = int.Parse(sprite.name);             spriteDict.Add(id, sprite);         }     }                              public static Sprite LoadSprite(int number)     {         return spriteDict[number];     } }
 
  |