CamRatioAdapter.cs
纵向2D游戏的屏幕宽度匹配
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
// 本脚本适用于纵向2D游戏的屏幕宽度匹配
// 如果是横向卷轴游戏,不需要用这个脚本
public class CamRatioAdapter : MonoBehaviour
{
// 测试结果:屏幕宽高比3:4 = 0.75对应摄像机size3.75
// 屏幕宽高比9:16 = 0.5625对应摄像机大小5
[Tooltip("屏幕宽/高")]
[SerializeField]
float ratio1 = 0.75f;
[Tooltip("正交摄像机Size")]
[SerializeField]
float size1 = 3.75f;
Camera cam;
void Update()
{
// 获取屏幕比例,计算正交摄像机的size
float curRatio = (float)Screen.width / Screen.height;
// 摄像机默认适配高度。当宽高比与摄像机size成反比时,能得到适配宽度的结果
float a = ratio1 * size1;
float size = a / curRatio;
cam = GetComponent<Camera>();
cam.orthographicSize = size;
}
}
2D游戏多图裁剪,设置层级
碰撞
灵敏度
PlayerCha.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class PlayerCha : MonoBehaviour
{
public float speed = 3;
// Start is called before the first frame update
void Start()
{
}
// Update is called once per frame
void Update()
{
}
public void Move(Vector3 input)
{
transform.position += input * speed * Time.deltaTime;
}
}
PlayerControl.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class PlayerControl : MonoBehaviour
{
PlayerCha player;
// Start is called before the first frame update
void Start()
{
player = GetComponent<PlayerCha>();
}
// Update is called once per frame
void Update()
{
float h = Input.GetAxis("Horizontal");
float v = Input.GetAxis("Vertical");
Vector3 move = new Vector3(h, v, 0);
//调用角色的移动函数
player.Move(move);
}
}
Texture Repeat
借用图形学知识
采用3D的Quad做2D的背景(Unity用图片做2D背景有Bug)
sharedMaterial&Material
BGMove.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class BGMove : MonoBehaviour
{
Material mat;
public float speed;
// Start is called before the first frame update
void Start()
{
mat = GetComponent<MeshRenderer>().sharedMaterial;
}
// Update is called once per frame
void Update()
{
mat.mainTextureOffset += new Vector2(0, speed * Time.deltaTime);
}
}
Bullet.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Bullet : MonoBehaviour
{
public float speed = 6;
// Start is called before the first frame update
void Start()
{
}
// Update is called once per frame
void Update()
{
transform.position += Vector3.up * speed * Time.deltaTime;
}
private void OnTriggerEnter2D(Collider2D collision)
{
Destroy(gameObject);
}
}
层碰撞矩阵
EnemyBeHit.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class EnemyBeHit : MonoBehaviour
{
SpriteRenderer[] renders;
public float redTime; //保持红色的时间
float changeColorTime; //变回白色的时间
void Start()
{
renders = GetComponentsInChildren<SpriteRenderer>();
}
private void Update()
{
if (Time.time >= changeColorTime)
{
SetColor(Color.white);
}
}
void SetColor(Color c)
{
if(renders[0].color == c)
{
return;
}
foreach(var r in renders)
{
r.color = c;
}
}
private void OnTriggerEnter2D(Collider2D collision)
{
SetColor(Color.red);
changeColorTime = Time.time + redTime;
}
}
创建爆炸动画
裁剪
Grid By Cell Count 4x4:
创建Square,创建Animation取名为Boom,把裁剪好的图片全选拖入Animation窗口,把Boom拖入Square,随机选一张爆炸图修改Square初始图片
添加脚本AnimEvent.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class AnimEvent : MonoBehaviour
{
public void Destroy()
{
Destroy(gameObject);
}
}
EnemyBeHit.cs
//播放爆炸特效
Instantiate(prefabBoom, transform.position, Quaternion.identity);
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class EnemyBeHit : MonoBehaviour
{
SpriteRenderer[] renders;
public float redTime = 0.1f; //保持红色的时间
float changeColorTime; //变回白色的时间
public float hp = 10;
public Transform prefabBoom; //爆炸动画
void Start()
{
renders = GetComponentsInChildren<SpriteRenderer>();
}
private void Update()
{
if (Time.time >= changeColorTime)
{
SetColor(Color.white);
}
}
void SetColor(Color c)
{
if(renders[0].color == c)
{
return;
}
foreach(var r in renders)
{
r.color = c;
}
}
private void OnTriggerEnter2D(Collider2D collision)
{
SetColor(Color.red);
changeColorTime = Time.time + redTime;
hp -= 1;
if (hp <= 0)
{
//死亡
Destroy(gameObject);
//播放爆炸特效
Instantiate(prefabBoom, transform.position, Quaternion.identity);
}
}
}
添加小怪
照之前的方法切割,拼图
更改压缩质量、去除磨皮效果
Before:
Now:
同样添加组件
并修改层级
写个Enemy Move脚本,挂到小怪兽上
EnemyMove.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class EnemyMove : MonoBehaviour
{
public float speed = 3;
void Start()
{
}
// Update is called once per frame
void Update()
{
transform.position += Vector3.down * speed * Time.deltaTime;
}
}
为了防止子弹一直存在,积累太多,写个脚本挂在子弹上,4s后自动销毁
DelayDestroy.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class DelayDestroy : MonoBehaviour
{
// Start is called before the first frame update
public float time = 1;
void Start()
{
StartCoroutine(Delay());
}
IEnumerator Delay()
{
yield return new WaitForSeconds(time);
Destroy(gameObject);
}
}
为了防止玩家出框,给玩家设置边界(左右2.3,上下4.3)并加上[Serializable]特性使边界可编辑
写个Border类放到Border.cs脚本里
Border.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Border : MonoBehaviour
{
public float top = 4.3f;
public float bottom = -4.3f;
public float left = -2.3f;
public float right = 2.3f;
}
在PlayerCha.cs中调用Border
//移动的边界范围
Border moveBorder;//Clamp:限定范围
pos.x = Mathf.Clamp(pos.x, moveBorder.left, moveBorder.right);
pos.y = Mathf.Clamp(pos.y, moveBorder.bottom, moveBorder.top);transform.position = pos;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class PlayerCha : MonoBehaviour
{
public Transform prefabBullet;
public float speed = 3;
public float fireCD = 0.2f;
float lastFireTime;
//移动的边界范围
Border moveBorder;
void Start()
{
}
// Update is called once per frame
void Update()
{
}
public void Move(Vector3 input)
{
Vector3 pos = transform.position + input * speed * Time.deltaTime;
//Clamp:限定范围
pos.x = Mathf.Clamp(pos.x, moveBorder.left, moveBorder.right);
pos.y = Mathf.Clamp(pos.y, moveBorder.bottom, moveBorder.top);
transform.position = pos;
//transform.position += input * speed * Time.deltaTime;
}
public void Fire()
{
if(Time.time < lastFireTime + fireCD)
{
return;
}
Vector3 pos = transform.position + new Vector3(0, 0.8f, 0);
Transform bullet = Instantiate(prefabBullet, pos, Quaternion.identity);
lastFireTime = Time.time;
}
}
创建怪物营地
创建空物体Spawner
Spawner.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Spawner : MonoBehaviour
{
//多个怪物的预制体
public List<Transform> monsters;
public float minTime = 0.5f;
public float maxTime = 2;
// Start is called before the first frame update
void Start()
{
StartCoroutine(Create());
}
IEnumerator Create()
{
while (true)
{
//创建怪物
//随机怪物下标
int i = Random.Range(0, monsters.Count);
//随机位置
Vector3 pos = new Vector3(Random.Range(-2.1f,2.1f), transform.position.y, 0);
Transform m = Instantiate(monsters[i], pos, Quaternion.identity);
//等一段时间
float t = Random.Range(minTime, maxTime);
yield return new WaitForSeconds(t);
}
}
// Update is called once per frame
void Update()
{
}
}
把脚本挂在空物体上,把前面做好的 小怪兽拖到Monsters列表里
让大怪兽左右飞行
BossMove.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class BossMove : MonoBehaviour
{
//枚举状态,定义在类之内
enum State
{
Down,
LeftRight,
}
State state = State.Down;
public float minY = 2.6f;
public float minX = -2.3f;
public float maxX = 2.3f;
public float speed = 3f;
void Start()
{
}
// Update is called once per frame
void Update()
{
switch (state)
{
case State.Down:
{
transform.position += Vector3.down * 3 * Time.deltaTime;
if(transform.position.y < minY)
{
state = State.LeftRight;
}
}
break;
case State.LeftRight:
{
transform.position += Vector3.left * speed * Time.deltaTime;
if (transform.position.x < minX)
{
speed = -Mathf.Abs(speed);
}
if (transform.position.x > maxX)
{
speed = Mathf.Abs(speed);
}
//if (transform.position.x < minX || transform.position.x > maxX)
//{
// speed *= -1;
//}
}
break;
}
}
}
全部脚本
PlayerControl.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class PlayerControl : MonoBehaviour
{
PlayerCha player;
// Start is called before the first frame update
void Start()
{
player = GetComponent<PlayerCha>();
}
// Update is called once per frame
void Update()
{
float h = Input.GetAxis("Horizontal");
float v = Input.GetAxis("Vertical");
Vector3 move = new Vector3(h, v, 0);
//调用角色移动函数
player.Move(move);
if (Input.GetButton("Fire1"))
{
player.Fire();
}
}
}
PlayerCha.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class PlayerCha : MonoBehaviour
{
public Transform prefabBullet;
public float speed = 3;
public float fireCD = 0.2f;
float lastFireTime;
//移动的边界范围
public Border moveBorder;
void Start()
{
}
// Update is called once per frame
void Update()
{
}
public void Move(Vector3 input)
{
Vector3 pos = transform.position + input * speed * Time.deltaTime;
//Clamp:限定范围
pos.x = Mathf.Clamp(pos.x, moveBorder.left, moveBorder.right);
pos.y = Mathf.Clamp(pos.y, moveBorder.bottom, moveBorder.top);
transform.position = pos;
//transform.position += input * speed * Time.deltaTime;
}
public void Fire()
{
if(Time.time < lastFireTime + fireCD)
{
return;
}
Vector3 pos = transform.position + new Vector3(0, 0.8f, 0);
Transform bullet = Instantiate(prefabBullet, pos, Quaternion.identity);
lastFireTime = Time.time;
}
private void OnTriggerEnter2D(Collider2D collision)
{
Debug.Log("玩家碰到怪物");
Destroy(gameObject);
}
}
Bullet.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Bullet : MonoBehaviour
{
public float speed = 6;
public Transform prefabExpl; //爆炸图的预制体
// Start is called before the first frame update
void Start()
{
}
// Update is called once per frame
void Update()
{
transform.position += Vector3.up * speed * Time.deltaTime;
}
private void OnTriggerEnter2D(Collider2D collision)
{
Destroy(gameObject);
//创建爆炸图
if (prefabExpl) //判断变量指向物体,且物体存在
{
Transform expl = Instantiate(prefabExpl, transform.position, Quaternion.identity);
}
}
}
EnemyBeHit.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class EnemyBeHit : MonoBehaviour
{
SpriteRenderer[] renders;
public float redTime = 0.1f; //保持红色的时间
float changeColorTime; //变回白色的时间
public float hp = 10;
public Transform prefabBoom; //爆炸动画
void Start()
{
renders = GetComponentsInChildren<SpriteRenderer>();
}
private void Update()
{
if (Time.time >= changeColorTime)
{
SetColor(Color.white);
}
}
void SetColor(Color c)
{
if(renders[0].color == c)
{
return;
}
foreach(var r in renders)
{
r.color = c;
}
}
private void OnTriggerEnter2D(Collider2D collision)
{
SetColor(Color.red);
changeColorTime = Time.time + redTime;
hp -= 1;
if (hp <= 0)
{
//死亡
Destroy(gameObject);
//播放爆炸特效
Instantiate(prefabBoom, transform.position, Quaternion.identity);
}
}
}
BoseMove.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class BossMove : MonoBehaviour
{
//枚举状态,定义在类之内
enum State
{
Down,
LeftRight,
}
State state = State.Down;
public float minY = 2.6f;
public float minX = -2.3f;
public float maxX = 2.3f;
public float speed = 3f;
void Start()
{
}
// Update is called once per frame
void Update()
{
switch (state)
{
case State.Down:
{
transform.position += Vector3.down * 3 * Time.deltaTime;
if(transform.position.y < minY)
{
state = State.LeftRight;
}
}
break;
case State.LeftRight:
{
transform.position += Vector3.left * speed * Time.deltaTime;
if (transform.position.x < minX)
{
speed = -Mathf.Abs(speed);
}
if (transform.position.x > maxX)
{
speed = Mathf.Abs(speed);
}
//if (transform.position.x < minX || transform.position.x > maxX)
//{
// speed *= -1;
//}
}
break;
}
}
}
EnemyMove.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class EnemyMove : MonoBehaviour
{
public float speed = 3;
void Start()
{
}
// Update is called once per frame
void Update()
{
transform.position += Vector3.down * speed * Time.deltaTime;
if(transform.position.y < -10 ||transform.position.y > 10)
{
Destroy(gameObject);
}
}
}
BGMove.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class BGMove : MonoBehaviour
{
Material mat;
public float speed;
// Start is called before the first frame update
void Start()
{
mat = GetComponent<MeshRenderer>().sharedMaterial;
}
// Update is called once per frame
void Update()
{
mat.mainTextureOffset += new Vector2(0, speed * Time.deltaTime);
}
}
Spawner.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Spawner : MonoBehaviour
{
//多个怪物的预制体
public List<Transform> monsters;
public float minTime = 0.5f;
public float maxTime = 2;
// Start is called before the first frame update
void Start()
{
StartCoroutine(Create());
}
IEnumerator Create()
{
while (true)
{
//创建怪物
//随机怪物下标
int i = Random.Range(0, monsters.Count);
//随机位置
Vector3 pos = new Vector3(Random.Range(-2.1f,2.1f), transform.position.y, 0);
Transform m = Instantiate(monsters[i], pos, Quaternion.identity);
//等一段时间
float t = Random.Range(minTime, maxTime);
yield return new WaitForSeconds(t);
}
}
// Update is called once per frame
void Update()
{
}
}
Border.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using System;
[Serializable]
public class Border : MonoBehaviour
{
public float top = 4.3f;
public float bottom = -4.3f;
public float left = -2.3f;
public float right = 2.3f;
}
AnimEvent.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class AnimEvent : MonoBehaviour
{
public void Destroy()
{
Destroy(gameObject);
}
}
DelayDestroy.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class DelayDestroy : MonoBehaviour
{
// Start is called before the first frame update
public float time = 1;
void Start()
{
StartCoroutine(Delay());
}
IEnumerator Delay()
{
yield return new WaitForSeconds(time);
Destroy(gameObject);
}
}
CamRatioAdapter.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
// 本脚本适用于纵向2D游戏的屏幕宽度匹配
// 如果是横向卷轴游戏,不需要用这个脚本
public class CamRatioAdapter : MonoBehaviour
{
// 测试结果:屏幕宽高比3:4 = 0.75对应摄像机size3.75
// 屏幕宽高比9:16 = 0.5625对应摄像机大小5
[Tooltip("屏幕宽/高")]
[SerializeField]
float ratio1 = 0.75f;
[Tooltip("正交摄像机Size")]
[SerializeField]
float size1 = 3.75f;
Camera cam;
void Update()
{
// 获取屏幕比例,计算正交摄像机的size
float curRatio = (float)Screen.width / Screen.height;
// 摄像机默认适配高度。当宽高比与摄像机size成反比时,能得到适配宽度的结果
float a = ratio1 * size1;
float size = a / curRatio;
cam = GetComponent<Camera>();
cam.orthographicSize = size;
}
}
附:开始界面(场景切换)
设置开始界面为0,游戏场景为1
StartScene.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.SceneManagement;
public class StartScene : MonoBehaviour
{
public void OnBtnStart()
{
SceneManager.LoadScene(1);
}
}
创建空物体GameObject
把StartScene.cs拖进去
把空物体拖到Button的Onclick下,并修改事件为OnBtnStart
Unity UI ---- Canvas 随画面缩放
设置相对位置(锚点)
播放音效、闪烁等
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.SceneManagement;
public class StartScene : MonoBehaviour
{
public AudioClip startSound; //开始按钮音效
AudioSource audioSource;
void Start()
{
audioSource = GetComponent<AudioSource>();
}
public void OnstartButton()
{
//播放音效
audioSource.clip = startSound;
audioSource.Play();
//开启协程
StartCoroutine(CoGameStart());
}
IEnumerator CoGameStart()
{
GameObject btn = GameObject.Find("开始按钮");
//开始按钮闪烁
for(int i = 0; i < 6; i++)
{
//隐藏/显示
btn.SetActive(!btn.activeInHierarchy);
yield return new WaitForSeconds(0.3f);
}
//切换场景
Debug.Log("切换场景");
SceneManager.LoadScene(1);
}
}