通过各种途径获得相关UI元素,在Unity中左侧栏中利用2D Project中元素完成场景。设置好各个物体所需的Box Collider 2d (碰撞)和 rigidBody 2d(物理属性)。设置好不同物体的类型(tag)(例如角色、金币、怪物、子弹等)后续碰撞检测需要使用。
主场景
进入游戏界面
失败和成功界面
其中还包括设置游戏内怪物和角色动画。角色为UNITY社区中下载的包,已包含动画。怪物使用关键帧动画,创建动画后,在ctri+6呼出的时间轴中添加关键帧。
在项目下新建Scriipt(代码)文件夹,创建GameManager的C#文件。在该文件中制定游戏大体规则。
使用单例模式(饿汉子单例模式)创建各种变量。
public class GameManage : MonoBehaviour
{
//单例模式(饿汉子单例模式)
public static GameManage Index; //静态变量不能被实例化
GameManage()
{
Index = this;
}
//工厂模式(内部所有组成成员的变量全部都是静态变量)
[Header("金币值")]
public int gold = 0;
[Header("当前关卡")]
public int Level = 1;
///
/// 所有的音频管理
///
public AudioSource[] allAudio;
///
/// 地面
///
public GameObject Floor;
///
/// 金币显示
///
public Text Gold;
///
/// 剩余时间显示
///
public Text TimeShow;
float times = 300;
///
/// 主角速度
///
public float PlayerSpeed = 0.03f;
///
/// 主角
///
public GameObject KyaRa;
///
/// 主角状态 0为无防护
///
public int PlayerState = 0;
///
/// 显示无敌图标
///
public GameObject PlayerIsSuper;
///
/// 显示主角武器
///
public GameObject PlayerIsAttact;
// Start is called before the first frame update
void Start()
{
PlayerIsSuper.SetActive(false);
PlayerIsSuper.SetActive(false);
}
// Update is called once per frame
void Update()
{
times -= Time.deltaTime;
TimeShow.text = times.ToString();
}
}
总体分为几种功能
1、角色的各种状态(创建各种变量标识主角状态)
//动画控制
public Animator anime;
//物理属性
public Rigidbody2D rigi;
//检测是否在地面上(false为不在地面上)
bool IsOnFloor = false;
//当前人物位置
float PlayerPrint;
//是否人物移动
bool isPlayerRun = true;
///
/// 武器
///
public GameObject Attact;
2、角色控制相关(通过键盘按键WASD移动,J攻击操控角色)移动主要使用transform方法改变主角位置。在主角行走至屏幕中间时,改为地图进行相对向后运动,主角位置不变。
void run()
{
//点击D键向右移动
if (Input.GetKey(KeyCode.D))
{
if (isPlayerRun)
{
//控制跑动速度和位置
transform.Translate(GameManage.Index.PlayerSpeed* - 1, 0, 0);
}
else
{
GameManage.Index.Floor.transform.Translate(GameManage.Index.PlayerSpeed * -1, 0, 0);
}
//控制人物面朝方向为向右
transform.eulerAngles = new Vector3(0, 180, 0);
if (IsOnFloor )
{
//播放跑步动画
anime.Play("run");
}
}
//松开前进键播放站立动画
if (Input.GetKeyUp(KeyCode.D))
{
anime.Play("idle");
}
//点击A键向左移动
if (Input.GetKey(KeyCode.A))
{
transform.Translate(GameManage.Index.PlayerSpeed * -1, 0, 0);
//控制人物面朝方向为向左
transform.eulerAngles = new Vector3(0, 0, 0);
if (IsOnFloor)
{
//播放跑步动画
anime.Play("run");
}
}
//松开前进键播放站立动画
if (Input.GetKeyUp(KeyCode.A))
{
anime.Play("idle");
}
//点击W键控制跳跃
if (IsOnFloor)
{
if (Input.GetKeyDown(KeyCode.W))
{
//给定一个向上的力
rigi.AddForce(new Vector2(0, 400));
anime.Play("jump");
}
}
//松开上键播放站立动画
if (Input.GetKeyUp(KeyCode.W))
{
anime.Play("idle");
}
}
此外还要设定主角在跳跃过程中不能再进行二次跳跃。在开头变量中设置有变量角色是否在地面上(利用碰撞信息进行判定)在角色控制中使用相关控制语句,在下方的碰撞检测中更改相关变量。
3、角色攻击相关
在角色吃到特定物品后,将GamaManager中的主角状态变量更改,在判断主角状态为可攻击状态时,可以进行攻击。
角色攻击方法(创建一个子弹,并利用rigidBody 2d给其一个力使其移动,子弹的消失和碰撞到怪物杀死怪物消失,后续进行解释)创建子弹使用Instantiate方法,在主角当前位置创建。
void Attacts()
{
if (Input.GetKeyDown(KeyCode.J))
{
if (GameManage.Index.PlayerState == 1)
{
GameObject ss =
Instantiate(Attact, this.transform.position , this.transform.rotation );//在player下创建子弹
ss.GetComponent<Rigidbody2D>().AddForce(this.transform.right*600*-1);
}
}
}
4、主角死亡的判定
利用碰撞检测和得到的当前主角状态(若为无敌状态则不死亡)进行条件判断,判断角色是否死亡,利用switch判断。
private void OnCollisionEnter2D(Collision2D collision)
{
//Debug.Log(collision.collider.tag);
if (collision.collider.tag == "Monster")
{
//主角死亡
switch (GameManage.Index.PlayerState)
{
case 0:
anime.Play("die");
Die();
break;
case 1:
anime.Play("die");
Die();
break;
case 2:
Debug.Log("您现在是无敌状态");
break;
}
}
IsOnFloor = true;
//anime.Play("idle");
}
5、角色死亡方法(角色死亡为触碰到怪物)
主体死亡方法为停止播放当前动画,并播放死亡动画和音频,并利用延迟函数invoke调用两个独立的死亡方法。
public void Die()
{
GameManage.Index.allAudio[0].Stop();
GameManage.Index.allAudio[4].Play();
anime.Play("die");
Invoke("Die1", 0.5f);
Invoke("Die2", 4f);
}
主角死亡后向下掉落出地图(通过改变BoxCollider 2D 的isTrigger属性为true,让主角下落)
void Die1()
{
this.gameObject.GetComponent<BoxCollider2D>().isTrigger = true;
}
主角死亡后界面需要跳转
void Die2()
{
SceneManager.LoadScene("fail");
}
1、各种变量(包括记录怪物种类,怪物生命值等的各种变量)
public int MonsterState = 0;//0是普通怪物 1是火圈 2是boss
public Animator anime;
public GameObject Fees;
public float times = 2;
public int BossLive = 10;
2、设置不同怪物的移动方式和出场方法(普通怪物在和角色相距10f时开始向主角移动,boss在和角色相距10f是开始发射小型怪物)怪物发射小型怪物原理和上方主角发射攻击原理相同。
if (MonsterState == 0)
{
if (Vector3.Distance(GameManage.Index.KyaRa.transform.position, this.transform.position) <= 10f)
{
this.transform.Translate(-0.03f, 0, 0);
}
}
else if (MonsterState == 2)
{
if (Vector3.Distance(GameManage.Index.KyaRa.transform.position, this.transform.position) <= 10f)
{
times -= Time.deltaTime;
if (times < 0)
{
GameObject ss =Instantiate(Fees, this.transform.position, this.transform.rotation);//在player下创建子弹
ss.GetComponent<Rigidbody2D>().AddForce(this.transform.right * 100 );
times = 3;
}
}
}
3、怪物死亡判定。(根据怪物状态,不同怪物情况不同,利用碰撞信息判定,判定触碰到无敌状态下的player或子弹都会死亡,boss设定攻击次数。)利用tag辅助判断到底触碰到了什么物体。
void OnCollisionEnter2D(Collision2D collision)
{
if (collision.collider.tag == "Player")
{
if (GameManage.Index.PlayerState == 2)
{
Debug.Log("自己死亡");
this.gameObject.GetComponent<BoxCollider2D>().isTrigger = true;
GameManage.Index.allAudio[3].Play();
}
}
else if (collision.collider.tag == "Attacts" && MonsterState == 0)
{
Debug.Log("自己死亡");
Destroy(this.gameObject);
GameManage.Index.allAudio[3].Play();
GameManage.Index.gold += 10;
GameManage.Index.Gold.text = GameManage.Index.gold.ToString();
}
else if (collision.collider.tag == "Attacts" && MonsterState == 2)
{
BossLive = BossLive - 1;
if (BossLive == 0)
{
Debug.Log("自己死亡");
Destroy(this.gameObject);
GameManage.Index.allAudio[3].Play();
GameManage.Index.gold += 100;
GameManage.Index.Gold.text = GameManage.Index.gold.ToString();
}
}
else if (collision.collider.tag == "Player" && MonsterState == 1 && GameManage.Index.PlayerState <= 2)
{
anime.Play("die");
collision.gameObject.GetComponent<Players>().Die();
}
}
}
1、设置变量(包括种类标识信息等)
public int state = 0;//0代表金币1代表武器2代表无敌3代表子弹4代表火圈5代表传送门
int OldState = 0;
2、根据情况设置不同的碰撞出发(OnTriggerEnter2D、OnCollisionEnter2D等,并根据tag辅助判断碰撞物为角色)
private void Start()
{
if (state == 3)
{
Invoke("Des", 1f);
}
}
void Des()
{
Destroy(this.gameObject);
}
private void Update()
{
if (state == 4)
{
transform.Rotate(0, 0, 0.3f);
}
}
//碰撞检测中的触发器,进入触发器区域
private void OnTriggerEnter2D(Collider2D collision)
{
if (collision.tag == "Player" && state == 0)
{
GameManage.Index.gold += 1;
GameManage.Index.Gold.text = GameManage.Index.gold.ToString();
GameManage.Index.allAudio[1].Play();
Destroy(this.gameObject);
}
else if (collision.tag == "Player" && state == 2)
{
GameManage.Index.allAudio[5].Play();
OldState = GameManage.Index.PlayerState;
GameManage.Index.PlayerState = 2;
GameManage.Index.PlayerIsSuper.SetActive(true);
Invoke("SuperPlayerOver", 5f);
Destroy(this.gameObject.GetComponent<SpriteRenderer>());//在没有删除物体的情况下,不让其显示
Destroy(this.gameObject.GetComponent<BoxCollider2D>());
}
else if (collision.tag == "Player" && state == 1)
{
GameManage.Index.allAudio[2].Play();
GameManage.Index.PlayerIsAttact.SetActive(true);
GameManage.Index.PlayerState = 1;
Destroy(this.gameObject);
}
else if (collision.tag == "Player" && state == 5)
{
SceneManager.LoadScene("clear");
}
}
private void OnCollisionEnter2D(Collision2D collision)
{
if (collision.collider.tag == "Monster")
{
Destroy(this.gameObject);
}
}
void SuperPlayerOver()
{
GameManage.Index.PlayerState = 2;
GameManage.Index.PlayerState = OldState;
GameManage.Index.allAudio[5].Stop();
GameManage.Index.PlayerIsSuper.SetActive(false);//设置是否显示一个物体
Destroy(this.gameObject);
}
按钮界面的跳转使用了代码进行完成
using UnityEngine.SceneManagement;
public class Login : MonoBehaviour
{
//跳转到制定程序
public void GotoScene(string Name)
{
SceneManager.LoadScene(Name);
}
}
并需要在unity中设置按钮相关的属性来实现跳转。
在代码头加入using UnityEngine.SceneManagement;后即可直接利用下方语句直接跳转至相应界面
游戏内死亡和完成游戏,为一定条件下直接进行页面跳转,
在代码头加入using UnityEngine.SceneManagement;后即可直接利用下方语句直接跳转至相应界面
SceneManager.LoadScene("fail");
注意:页面跳转需要在unity中building 里将所有SCENE添加到列表中才能实现!!!
发布为webgl版本。在building中选择webgl,根据提示下载相关组件,并进行项目的转换。转换完成后选择building即可。
注意:项目所有的文件名和路径名称必须全部为英文。
注意:使用火狐浏览器打开,还需设置其webgl.force-enable为true
security.fileuri.strict_origin_policy为false才可运行。界面如下:
2d横版游戏demo展示