github项目地址
视频演示地址
订阅-发布模式:
订阅者把自己想订阅的事件注册到调度中心,当该事件触发时候,发布者发布该事件到调度中心,由调度中心统一调度订阅者注册到调度中心的处理代码
在这个游戏中,记分员类就充当发布者角色,其他涉及加分的事件对它进行订阅,这样每次加分事件发生统一由记分员完成加分,不需要各个类分别处理,从而减少耦合,达成优化目的。
这里只是给出几个截图,具体游戏情况见视频演示(地址上面已给出)
游戏场景俯瞰图
玩家距离巡逻兵很远的时候会,巡逻兵巡逻(播放行走动画)
玩家靠近巡逻兵(进入他的感知范围),巡逻兵追捕玩家(播放奔跑动画)
玩家跳跃从而躲避巡逻兵(这个图好难截啊)
巡逻兵追到玩家,播放攻击动画,unity酱播放倒地动画
预设准备:
动作控制设置:
unity酱的动作控制如下:
战龙动作控制如下:
编写代码:
unity酱控制代码:
主要是几个动作的函数,提供给firstSceneController调用:
using UnityEngine;
using System.Collections;
// 必要なコンポーネントの列記
[RequireComponent(typeof(Animator))]
[RequireComponent(typeof(CapsuleCollider))]
[RequireComponent(typeof(Rigidbody))]
public class Player
{
GameObject myGirl;
private bool isJump = false;
public float animSpeed = 1.3f;
public float jumpAnimSpeed = 1f;
public float lookSmoother = 3.0f;
public bool useCurves = true;
public float useCurvesHeight = 1f;
// 前進速度
public float forwardSpeed = 7.0f;
// 後退速度
public float backwardSpeed = 5.0f;
// 旋转速度
public float rotateSpeed = 1.3f;
// 跳跃时施加的力
public float jumpPower = 5.0f;
private CapsuleCollider col;
private Rigidbody rb;
private Vector3 velocity;
private float orgColHight;
private Vector3 orgVectColCenter;
private Animator anim;
private AnimatorStateInfo currentBaseState;
static int idleState = Animator.StringToHash("Base Layer.Idle");
static int locoState = Animator.StringToHash("Base Layer.Locomotion");
static int jumpState = Animator.StringToHash("Base Layer.Jump");
static int restState = Animator.StringToHash("Base Layer.Rest");
public Player()
{
myGirl = GameObject.FindWithTag("Player");
anim = myGirl.GetComponent();
col = myGirl.GetComponent();
rb = myGirl.GetComponent();
orgColHight = col.height;
orgVectColCenter = col.center;
}
public void run(float h, float v)
{
anim.SetFloat("Speed", v);
anim.SetFloat("Direction", h);
rb.useGravity = true;
velocity = new Vector3(0, 0, v);
velocity = myGirl.transform.TransformDirection(velocity);
if (v > 0.1)
{
velocity *= forwardSpeed;
}
else if (v < -0.1)
{
velocity *= backwardSpeed;
}
myGirl.transform.localPosition += velocity * Time.fixedDeltaTime;
myGirl.transform.Rotate(0, h * rotateSpeed, 0);
}
public void jump()
{
if (isJump == false)
{
if (currentBaseState.fullPathHash == locoState)
{
if (!anim.IsInTransition(0))
{
rb.AddForce(Vector3.up * jumpPower, ForceMode.VelocityChange);
anim.SetBool("Jump", true);
}
}
}
}
public void cry()
{
if (myGirl.transform.position.y > 1)
{
myGirl.transform.position = new Vector3(myGirl.transform.position.x, 0, myGirl.transform.position.z);
}
anim.SetBool("GameOver",true);
}
public void myUpdate()
{
if (myGirl.transform.position.y <= 0)
{
isJump = false;
anim.speed = animSpeed;
}
else
{
anim.speed = jumpAnimSpeed;
}
currentBaseState = anim.GetCurrentAnimatorStateInfo(0);
if (currentBaseState.fullPathHash == locoState)
{
if (useCurves)
{
resetCollider();
}
}
else if (currentBaseState.fullPathHash == jumpState)
{
if (!anim.IsInTransition(0))
{
if (useCurves)
{
float jumpHeight = anim.GetFloat("JumpHeight");
float gravityControl = anim.GetFloat("GravityControl");
if (gravityControl > 0)
rb.useGravity = false;
Ray ray = new Ray(myGirl.transform.position + Vector3.up, -Vector3.up);
RaycastHit hitInfo = new RaycastHit();
if (Physics.Raycast(ray, out hitInfo))
{
if (hitInfo.distance > useCurvesHeight)
{
col.height = orgColHight - jumpHeight;
float adjCenterY = orgVectColCenter.y + jumpHeight;
col.center = new Vector3(0, adjCenterY, 0);
}
else
{
resetCollider();
}
}
}
anim.SetBool("Jump", false);
}
}
else if (currentBaseState.fullPathHash == idleState)
{
if (useCurves)
{
resetCollider();
}
if (Input.GetButtonDown("Jump"))
{
anim.SetBool("Rest", true);
}
}
else if (currentBaseState.fullPathHash == restState)
{
if (!anim.IsInTransition(0))
{
anim.SetBool("Rest", false);
}
}
}
void resetCollider()
{
col.height = orgColHight;
col.center = orgVectColCenter;
}
}
战龙控制代码:
主要是几个触发函数:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Monster : MonoBehaviour {
private Rigidbody rg;
private Animator anim;
private CapsuleCollider col;
private AnimatorStateInfo currentBaseState;
static int walkState = Animator.StringToHash("Base Layer.walk");
static int runState = Animator.StringToHash("Base Layer.run");
static int idleState = Animator.StringToHash("Base Layer.idle");
static int attackState = Animator.StringToHash("Base Layer.attack_1");
private float time;
private float timePassed;
private bool isRun = false;
private Vector3 pos;
private float length = 13;
private float walkSpeed = 3;
private float runSpeed = 5;
// Use this for initialization
void Start () {
anim = GetComponent();
col = GetComponent();
rg = GetComponent();
rg.useGravity = true;
anim.SetBool("IsWalk", true);
setTime();
}
private void FixedUpdate()
{
if (Director.getInstance().getFirstController().isGameOver())
{
return;
}
if (isRun == true)
{
anim.SetBool("IsRun",true);
Vector3 dir = pos - transform.position;
dir = dir.normalized;
Quaternion rotation = Quaternion.LookRotation(dir, Vector3.up);
rotation.x = 0;
rotation.z = 0;
transform.rotation = rotation;
Vector3 velocity0 = new Vector3(0, 0, runSpeed);
velocity0 = transform.TransformDirection(velocity0);
velocity0.y = 0;
transform.localPosition += velocity0 * Time.fixedDeltaTime;
return;
} else
{
anim.SetBool("IsRun", false);
}
if (timePassed >= time)
{
transform.Rotate(0, 90, 0);
timePassed = 0;
return;
}
Vector3 velocity = new Vector3(0, 0, walkSpeed);
velocity = transform.TransformDirection(velocity);
transform.localPosition += velocity * Time.fixedDeltaTime;
timePassed += Time.fixedDeltaTime;
}
private void OnCollisionEnter(Collision collision)
{
if (collision.gameObject.tag == "Wall")
{
isRun = false;
transform.Rotate(0, 90, 0);
setTime();
} else if (collision.gameObject.tag == "Player")
{
Director.getInstance().getFirstController().gameOver();
anim.SetBool("IsWalk",false);
anim.SetTrigger("Attack_1");
}
}
private void OnTriggerEnter(Collider other)
{
if (other.gameObject.tag == "Player")
{
isRun = true;
pos = other.gameObject.transform.position;
}
}
private void OnTriggerStay(Collider other)
{
if (other.gameObject.tag == "Player")
{
pos = other.gameObject.transform.position;
}
}
private void OnTriggerExit(Collider other)
{
if (other.gameObject.tag == "Player")
{
isRun = false;
ScoreRecorder.getInstance().addScore();
}
}
private void setTime()
{
System.Random random = new System.Random();
time = random.Next(2,3);
timePassed = 0;
}
}
战龙工厂:用于生产战龙(之前UML图解释部分已经做出详细解释,这里就不说了)
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class MonsterFactory {
private static MonsterFactory _instance;
private List monsters;
private GameObject dragon;
public static MonsterFactory getInstance() {
if (_instance == null)
{
_instance = new MonsterFactory();
}
return _instance;
}
public void setPrefabs(GameObject prefab)
{
dragon = prefab;
monsters = new List();
}
public GameObject getMonster()
{
for (int num = 0; num < monsters.Count; num++)
{
if (monsters[num].activeInHierarchy == false)
{
monsters[num].SetActive(true);
return monsters[num];
}
}
Debug.Log("tets");
monsters.Add(GameObject.Instantiate(dragon) as GameObject);
monsters[monsters.Count - 1].SetActive(true);
return monsters[monsters.Count - 1];
}
// 由于不需要销毁对象,所以free不用写
public void free(GameObject temp)
{
temp.SetActive(false);
}
}
场记代码 : 提供用户接口给UserInterface类调用
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.SceneManagement;
public class FirstController : MonoBehaviour, SceneController, UserAction
{
private bool isgameOver = false;
Player myGirl;
public GameObject monster;
private void Awake()
{
Director.getInstance().setFirstController(this);
MonsterFactory.getInstance().setPrefabs(monster);
}
// Use this for initialization
void Start () {
myGirl = new Player();
Director.getInstance().getFirstController().LoadResources();
}
private void Update()
{
myGirl.myUpdate();
}
public void LoadResources () {
GameObject temp1 = MonsterFactory.getInstance().getMonster();
temp1.transform.position = new Vector3(3,0,0);
GameObject temp2 = MonsterFactory.getInstance().getMonster();
temp2.transform.position = new Vector3(-3, 0, 0);
GameObject temp3 = MonsterFactory.getInstance().getMonster();
temp3.transform.position = new Vector3(3, 0, 10);
GameObject temp4 = MonsterFactory.getInstance().getMonster();
temp4.transform.position = new Vector3(-3, 0, 10);
}
public void movePlayer(float h, float v) {
myGirl.run(h, v);
}
public void jump() {
myGirl.jump();
}
public bool isGameOver() {
return isgameOver;
}
public void reStart() {
SceneManager.LoadScene(SceneManager.GetActiveScene().name);
ScoreRecorder.getInstance().setScore(0);
}
public void gameOver()
{
isgameOver = true;
myGirl.cry();
}
}
用户交换部分:UserInterface类–主要是接受并处理用户的前后左右输入、空格键输入、点击输入(重开游戏、游戏提示);
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
public class UserInterface : MonoBehaviour {
private ScoreRecorder scoreRecorder;
private UserAction action;
// Use this for initialization
void Start () {
action = Director.getInstance().getFirstController();
scoreRecorder = ScoreRecorder.getInstance();
}
// Update is called once per frame
void Update () {
if (action.isGameOver())
{
action.movePlayer(0, 0);
return;
}
if (Input.GetButtonDown("Jump"))
{
action.jump();
}
float h = Input.GetAxis("Horizontal");
float v = Input.GetAxis("Vertical");
action.movePlayer(h, v);
}
private void OnGUI()
{
// 游戏信息
GUI.Box(new Rect(Screen.width - 260, 10, 250, 110), "操作方法");
GUI.Label(new Rect(Screen.width - 245, 30, 250, 30), "上/下 键 : 向前跑/向后退");
GUI.Label(new Rect(Screen.width - 245, 50, 250, 30), "左/右 键 : 向左转/向右转");
GUI.Label(new Rect(Screen.width - 245, 70, 250, 30), "奔跑时按下空格键 : 跳跃");
GUI.Label(new Rect(Screen.width - 245, 90, 250, 30), "停下的时候按下空格键 : 卖萌");
string gameName = "躲避怪物";
string gameRules = "操作任务躲避怪物的追捕,每躲过一只怪物分数加一";
// 显示游戏名字和游戏规则信息
if (GUI.RepeatButton(new Rect(10, 10, 120, 20), gameName))
{
GUI.TextArea(new Rect(10, 40, Screen.width - 20, Screen.height / 2), gameRules);
}
// 重开游戏按钮
else if (GUI.Button(new Rect(140, 10, 70, 20), "重新开始"))
{
action.reStart();
}
if (action.isGameOver())
{
GUI.Box(new Rect(Screen.width - 340, 37, 70, 23), "游戏结束");
}
GUI.Box(new Rect(Screen.width - 340, 10, 70, 23), "分数:" + scoreRecorder.getScore());
}
}
记分员类: 这个类在前面的UML图中也说过了,就不说了
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class ScoreRecorder {
public static ScoreRecorder _instance;
private int Score = 0;
public void addScore()
{
Score++;
}
public int getScore()
{
return Score;
}
public void setScore(int num)
{
Score = num;
}
public static ScoreRecorder getInstance()
{
if (_instance == null)
{
_instance = new ScoreRecorder();
}
return _instance;
}
}
导演类:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Director : System.Object
{
private static Director _instance;
private FirstController firstSceneController;
public static Director getInstance()
{
if (_instance == null)
{
_instance = new Director();
}
return _instance;
}
public FirstController getFirstController()
{
return firstSceneController;
}
internal void setFirstController(FirstController gom)
{
if (null == firstSceneController)
{
firstSceneController = gom;
}
}
}
几个接口类
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public interface SceneController {
void LoadResources();
}
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public interface UserAction {
void movePlayer(float h, float v);
void jump();
bool isGameOver();
void reStart();
}
至此,游戏制作基本完成,我们还可用通过修改战龙/玩家的奔跑速度、战龙的巡逻范围、调整画面颜色等来增加游戏体验。