Unity3D第一人称射箭游戏作业

游戏要求

开发一个第一人称射箭游戏,应满足以下要求:

  •  基础分(2分):有博客;
  •  1-3分钟视频(2分):视频呈现游戏主要游玩过程;
  •  地形(2分):使用地形组件,上面有草、树;
  •  天空盒(2分):使用天空盒,天空可随玩家位置 或 时间变化 或 按特定按键切换天空盒;
  •  固定靶(2分):有一个以上固定的靶标;
  •  运动靶(2分):有一个以上运动靶标,运动轨迹,速度使用动画控制;
  •  射击位(2分):地图上应标记若干射击位,仅在射击位附近可以拉弓射击,每个位置有 n 次机会;
  •  驽弓动画(2分):支持蓄力半拉弓,然后 hold,择机 shoot;
  •  游走(2分):玩家的驽弓可在地图上游走,不能碰上树和靶标等障碍;
  •  碰撞与计分(2分):在射击位,射中靶标的相应分数,规则自定;

具体设计为:

-设置静止靶单倍区和运动靶双倍区两个区域可以射击,其他区域没有靶子无法射击得分。

-靶子分为白、黑、红自外向里为3、4、5分,集中运动靶分数则翻倍

-一共有10支箭矢,全部用完则游戏结束

-长按空格可以为弓弩蓄力,再次按下空格则发射

游戏效果展示

视频展示:

主要功能代码实现:

游戏总控:MainController

负责游戏一开始的初始化,和游戏进行中将信息传递到不同的脚本中,如为视图View传递分数、当前位置等信息。

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

/* 游戏状态,0为准备进行,1为正在进行游戏,2为结束 */
enum GameState {
    Ready = 0, Playing = 1, GameOver = 2
};

public class MainController : MonoBehaviour
{   private View view; // 游戏视图
    private CrossController crossController;//十字弩控制器
    private int gameState;//游戏状态
    public GUISkin gameSkin; //按键皮肤
    public Material[] skyboxMaterials;//存储天空盒数组
    private int index;//天空盒数组下标
    private int lessnum;//剩余箭矢数
    public GameObject cross; //十字弩
    // Start is called before the first frame update
    void Start()//在start中进行初始化
    {Director.GetInstance().mainController = this;
     crossController= gameObject.AddComponent<CrossController>();
     gameState = (int)GameState.Ready;
     view = gameObject.AddComponent<View>();
     view.gameSkin = gameSkin;
     // 隐藏十字弩
     cross.SetActive(false);
     //携程切换天空盒
     index=-1;
     StartCoroutine(ChangeSkybox());
    }

    public void SetGameState(int state) {//设置游戏状态
        gameState = state;  
    }

    public void Restart() {//重启游戏函数
        view.Init();
        lessnum=10;
        roundController.Reset();
        CrossC crossComponent = cross.GetComponent<CrossC>();
        crossComponent.Init();
    }

    public void ShowPage() {//控制视图view的页面
        switch(gameState) {
            case 0:
                view.ShowHomePage();
                break;
            case 1:
                view.ShowGamePage();
                break;
            case 2:
                view.ShowRestart();
                break;
        }
        }
    public void SetViewScore(int score) {//将十字弩的计分传给视图
        view.SetScore(score);
    }

    public void SetViewNum(int num) {//将十字弩的剩余箭矢数传给视图
        lessnum=num;
        view.SetNum(num);
    }

    public void SetViewX(float x){//将十字弩的位置传给视图
        view.GetX(x);
    }

    public void Update(){//实时传递游戏状态给视图
        CrossC crossComponent = cross.GetComponent<CrossC>();
        SetViewScore(crossComponent.getScore());
        SetViewNum(crossComponent.getNum());
        SetViewX(crossComponent.getX());
        if(lessnum==0) SetGameState(2);//剩余箭为0时结束游戏
    }

    IEnumerator ChangeSkybox()
{
    while (true)
    {
        // 循环选择一个天空盒子材质
        index = (index+1)%3;
        RenderSettings.skybox = skyboxMaterials[index];

        // 等待一段时间后再切换天空盒子
        yield return new WaitForSeconds(20);
    }
}

}

视图控制:View

负责控制游戏的页面提示,向玩家展示游戏的进行状态

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;

public class View : MonoBehaviour
{   
    private MainController mainController;
    private int score;//当前得分
    private int arrownum;//当前剩余箭矢数
    private float x;//当前位置
    public GUISkin gameSkin;

    // Start is called before the first frame update
    void Start()//初始化
    { score = 0;
      mainController = Director.GetInstance().mainController;
    }

    // 分别获取分数、剩余箭矢数和位置的函数
    public void SetScore(int score) {
        this.score = this.score+score;
    }

    public void SetNum(int num){
        arrownum=num;
    }

    public void GetX(float a){
        x=a;
    }

    public void Init(){//开始游戏的初始化
        score=0;
        arrownum=10;
    }
    public void ShowHomePage() {
        AddStartButton();
    }

    public void ShowGamePage(){
        AddGameLabel();
    }

    public void ShowRestart() {
        AddRestartlabel();
    }
    public void AddStartButton(){//GameState=0时,展示初始界面
        GUI.skin = gameSkin;
        if(GUI.Button(new Rect(340,200,160,80),"点击开始")){
            mainController.Restart();
            mainController.SetGameState((int)GameState.Playing);//按下开始按钮则进入游戏状态
        }
    }

    public void AddGameLabel() {//GameState=1时,展示游戏界面
        GUIStyle labelStyle = new GUIStyle();
        labelStyle.normal.textColor = Color.black;
        labelStyle.fontSize = 30;
        mainController.cross.SetActive(true);
        mainController.cross.transform.rotation=Quaternion.Euler(-10f, 0f, 0f);
        GUI.Label(new Rect(570, 10, 100, 50), "得分: " + score, labelStyle);//展示得分、剩余箭矢数及提示当前区域
        GUI.Label(new Rect(570, 45, 100, 50), "剩余箭数: " + arrownum, labelStyle);
        if(x<-9.96||(x>10.05&&x<19.00)||x>40.00) GUI.Label(new Rect(10,10,100,50),"当前区域:无箭靶",labelStyle);
        else if(x>=-9.96&&x<10.05) GUI.Label(new Rect(10,10,100,50),"当前区域:静止靶单倍区",labelStyle);
        else GUI.Label(new Rect(10,10,100,50),"当前区域:移动靶双倍区",labelStyle);
    }

    public void AddRestartlabel(){//GameState=2时,展示结束界面
        GUI.skin = gameSkin;
        GUIStyle labelStyle = new GUIStyle();
        labelStyle.normal.textColor = Color.red;
        labelStyle.fontSize = 30;
        GUI.Label(new Rect(340,100,100,50),"最终得分:"+score,labelStyle);//展示最终得分
        if(GUI.Button(new Rect(340,200,160,80),"再来一次")){
            mainController.Restart();
            mainController.SetGameState((int)GameState.Playing);//按下再来一次按钮则再次进入游戏状态
        }

    }

    void OnGUI() {
        mainController.ShowPage();
    }
}

十字弩:CrossC

挂载在十字弩物体上,控制十字弩的射击、蓄力等动画呈现,以及控制十字弩可以上下左右移动。

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;

public class CrossC : MonoBehaviour
{
    private Animator ani;
    public float horizontalinput;//水平参数
    public float Verticalinput;//垂直参数
    float speed=1.6f;//速度参数
    public float power;//蓄力系数
    public Arrow _arrow;//箭矢对象
    private Arrow arrow;
    private bool flag=false;
    private int num;
    private bool hold;//记录按下空格应该是拉弓蓄力还是发射
    private bool wait;
    public Text hintText;
    public Text powerText;

    void Start()//初始化
    {    ani=GetComponent<Animator>();
         hintText.gameObject.SetActive(false);
         powerText.gameObject.SetActive(false);
         num=10;
         hold=true; wait=true;
    }

    public void Init(){
        num=10;
    }

    // Update is called once per frame
    void Update()
    {   horizontalinput = Input.GetAxis("Horizontal");
        //AD方向控制
        Verticalinput = Input.GetAxis("Vertical");
 
        if (horizontalinput!=0&&Verticalinput!=0)
        {
            horizontalinput = horizontalinput * 1.4f;
            Verticalinput = Verticalinput * 1.4f;
        }
        //WS方向控制
        
        this.transform.Translate(Vector3.right * horizontalinput * Time.deltaTime * speed);
        //控制该物体向侧方移动
        this.transform.Translate(Vector3.up*  Verticalinput * Time.deltaTime * speed);
        if(iflegalarea()) {ShowHint("您已超出射击区域,该区域无射击靶"); flag=true;}
        else if(flag&&!iflegalarea()) {HideHint(); flag=false;}
        if(power!=0f) {
            int ppower=(int)(power*100);
            powerText.text="蓄力"+ppower+"%";
            powerText.gameObject.SetActive(true);
        }
        else{
            powerText.gameObject.SetActive(false);
        }
        //控制该物体向上下移动
        if(Input.GetKey(KeyCode.Space))//按下空格
        {  if(hold){//蓄力
            Transform childTransform = transform.Find("箭");
            childTransform.gameObject.SetActive(true);
            ani.SetBool("pull",true);
            ani.SetBool("shoot",false);
            if (power < 1)
                        {
                            power += Time.deltaTime * 0.5f;
                        }
                        else
                        {
                            power = 1;
                        }
            ani.SetFloat("pullpower",power);
            wait=true;
            }
            else{//发射
                 if(wait){ani.SetBool("shoot",true);
                 ani.SetBool("pull",false);
                 ani.SetBool("hold",false);
                 initArrow();
                 RunArrow();
                 wait=false;
                 Transform childTransform = transform.Find("箭");
                 childTransform.gameObject.SetActive(false);
                 power=0f;
                 ani.SetFloat("pullpower",power);
                 Invoke("SetHoldTrue", 1f);}
                }

            }
        
       if (Input.GetKeyUp(KeyCode.Space)){//抬起空格,保持
            ani.SetBool("pull",false);
            hold=false;//预备射击}
    }

    public void initArrow()//创建箭矢对象
    {
        Vector3 np=this.transform.position;
        arrow = Instantiate(_arrow,np,Quaternion.identity);
        arrow.gameObject.SetActive(true);
        
    }

    public void RunArrow()//发射箭矢对象
    {
        if(arrow != null)
        {
            Rigidbody rb = arrow.GetComponent<Rigidbody>();
            if (rb)
            {
                rb.drag = 0.5f;
                Vector3 shootingDirection = this.transform.forward;
                rb.AddForce(shootingDirection * (power*20f+5), ForceMode.Impulse);
                num--;
            }
        }
    }

    //向其他脚本传递数据
    public int getScore()//若箭矢命中,传回分数并提示
    {
        if(arrow != null && arrow.attacked&&!arrow.isRecord){
            arrow.isRecord=true;
            if(arrow.score==5) ShowHint("正中红心!",0);
            else ShowHint("命中!",0);
            float x=this.transform.position.x;
            if(x>=19.00&&x<=40.00) return arrow.score*2;
             else return arrow.score;
        }
        return 0;
    }
    public int getNum(){
        return num;
    }

    public float getX(){
        return this.transform.position.x;
    }

     public void ShowHint(string message,int state=1)//提示标签控制
    {
        // 显示提示文字
        hintText.text = message;
        hintText.gameObject.SetActive(true);

        // 延迟几秒后隐藏提示文字
        if(state==0) Invoke("HideHint", 1f);
    }

     void HideHint()
    {
        hintText.gameObject.SetActive(false);
    }

    void SetHoldTrue(){
           hold=true;
    }

    private bool iflegalarea(){//判断是否在射击区域,不在则提示
        float x=this.transform.position.x;
        return (x<-9.96||(x>10.05&&x<19.00)||x>40.00);
    }
}

箭矢:Arrow

挂载在箭矢物体上,主要负责响应碰撞事件并传回击中的分数:

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;

public class CrossC : MonoBehaviour
{
    private Animator ani;
    public float horizontalinput;//水平参数
    public float Verticalinput;//垂直参数
    float speed=1.6f;//速度参数
    public float power;//蓄力系数
    public Arrow _arrow;//箭矢对象
    private Arrow arrow;
    private bool flag=false;
    private int num;
    private bool hold;//记录按下空格应该是拉弓蓄力还是发射
    private bool wait;
    public Text hintText;
    public Text powerText;

    void Start()//初始化
    {    ani=GetComponent<Animator>();
         hintText.gameObject.SetActive(false);
         powerText.gameObject.SetActive(false);
         num=10;
         hold=true; wait=true;
    }

    public void Init(){
        num=10;
    }

    // Update is called once per frame
    void Update()
    {   horizontalinput = Input.GetAxis("Horizontal");
        //AD方向控制
        Verticalinput = Input.GetAxis("Vertical");
 
        if (horizontalinput!=0&&Verticalinput!=0)
        {
            horizontalinput = horizontalinput * 1.4f;
            Verticalinput = Verticalinput * 1.4f;
        }
        //WS方向控制
        
        this.transform.Translate(Vector3.right * horizontalinput * Time.deltaTime * speed);
        //控制该物体向侧方移动
        this.transform.Translate(Vector3.up*  Verticalinput * Time.deltaTime * speed);
        if(iflegalarea()) {ShowHint("您已超出射击区域,该区域无射击靶"); flag=true;}
        else if(flag&&!iflegalarea()) {HideHint(); flag=false;}
        if(power!=0f) {
            int ppower=(int)(power*100);
            powerText.text="蓄力"+ppower+"%";
            powerText.gameObject.SetActive(true);
        }
        else{
            powerText.gameObject.SetActive(false);
        }
        //控制该物体向上下移动
        if(Input.GetKey(KeyCode.Space))//按下空格
        {  if(hold){//蓄力
            Transform childTransform = transform.Find("箭");
            childTransform.gameObject.SetActive(true);
            ani.SetBool("pull",true);
            ani.SetBool("shoot",false);
            if (power < 1)
                        {
                            power += Time.deltaTime * 0.5f;
                        }
                        else
                        {
                            power = 1;
                        }
            ani.SetFloat("pullpower",power);
            wait=true;
            }
            else{//发射
                 if(wait){ani.SetBool("shoot",true);
                 ani.SetBool("pull",false);
                 ani.SetBool("hold",false);
                 initArrow();
                 RunArrow();
                 wait=false;
                 Transform childTransform = transform.Find("箭");
                 childTransform.gameObject.SetActive(false);
                 power=0f;
                 ani.SetFloat("pullpower",power);
                 Invoke("SetHoldTrue", 1f);}
                }

            }
        
       if (Input.GetKeyUp(KeyCode.Space)){//抬起空格,保持
            ani.SetBool("pull",false);
            hold=false;//预备射击}
    }

    public void initArrow()//创建箭矢对象
    {
        Vector3 np=this.transform.position;
        arrow = Instantiate(_arrow,np,Quaternion.identity);
        arrow.gameObject.SetActive(true);
        
    }

    public void RunArrow()//发射箭矢对象
    {
        if(arrow != null)
        {
            Rigidbody rb = arrow.GetComponent<Rigidbody>();
            if (rb)
            {
                rb.drag = 0.5f;
                Vector3 shootingDirection = this.transform.forward;
                rb.AddForce(shootingDirection * (power*20f+5), ForceMode.Impulse);
                num--;
            }
        }
    }

    //向其他脚本传递数据
    public int getScore()//若箭矢命中,传回分数并提示
    {
        if(arrow != null && arrow.attacked&&!arrow.isRecord){
            arrow.isRecord=true;
            if(arrow.score==5) ShowHint("正中红心!",0);
            else ShowHint("命中!",0);
            float x=this.transform.position.x;
            if(x>=19.00&&x<=40.00) return arrow.score*2;
             else return arrow.score;
        }
        return 0;
    }
    public int getNum(){
        return num;
    }

    public float getX(){
        return this.transform.position.x;
    }

     public void ShowHint(string message,int state=1)//提示标签控制
    {
        // 显示提示文字
        hintText.text = message;
        hintText.gameObject.SetActive(true);

        // 延迟几秒后隐藏提示文字
        if(state==0) Invoke("HideHint", 1f);
    }

     void HideHint()
    {
        hintText.gameObject.SetActive(false);
    }

    void SetHoldTrue(){
           hold=true;
    }

    private bool iflegalarea(){//判断是否在射击区域,不在则提示
        float x=this.transform.position.x;
        return (x<-9.96||(x>10.05&&x<19.00)||x>40.00);
    }
}

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值