Unity3D学习记录(跑酷小游戏第四阶段)

这磨人的小游戏项目也要接近尾声了,大概还差一两天的进度吧。最近在看佐佐木智广的《游戏剧本怎么写》,讲了很多Galgame的剧情、脚本设置,非常有意思的一本书,有时间我开个新坑来做一些读书笔记。下一个项目我也打算完成我以前能力不足未能写完的galgame框架,然后交给会剧本会画画的有缘人来操作(>A<),至于新课学习部分,明天离散数学书就来了,准备重点看预备知识巩固和图论部分,后面就做些题吧,暑假也就这样了。不多BB,继续撸项目


本日目标:

1.实现其他障碍物的触发检测
2.重写HeroControler,优化人物的动作

一.其他障碍物的设置

单单把障碍物放在那里让玩家去碰还是有点LOW了,我们加些脚本让障碍物有些变化,然后再加入昨天的动态道具创建,实现场景物体完全的动态生成。

(一)障碍物刺的制作

首先导入一个刺的模型,设置好比例尺,缩放到合适大小。我们来实现这样一个触发器常见效果:主人公走到刺的前面一段距离时,给刺发送信号,让其向玩家移动。还有不同方向的,也都来实现一遍。
在这里插入图片描述

在这里插入图片描述

这里的做法是使用AddComponent方法来动态添加脚本。先在刺的前面合适距离建一个空对象,作为一个触发装置,添加一个BoxCollider(IsTrigger打勾)。创建一个DynamicAddScript脚本,用于触发检测并给刺添加脚本,再创建一个控制物体移动的脚本ObjMovement。
DynamicAddScript脚本内容:

public class DynamicAddScripts : MonoBehaviour {
    public GameObject GoNeedObject;
    //定义添加脚本的对象
 // Use this for initialization
  void Start () {
  }
    private void OnTriggerEnter(Collider col)
    {
        if(GoNeedObject)//string.isnullorempty
        {
            GoNeedObject.AddComponent<MovingObj>();
        }
    }
    // Update is called once per frame
    void Update () {
    	}
    }

ObjMovement脚本写一个简单的移动代码就能看到效果了

this.transform.Translate(Vector3.back * FloMovingSpeed,Space.World);

再来写一个垂直上下移动的刺,添加触发检测和脚本的方法一样,来写一下上下移动的逻辑。需要事先Debug找到合适的最高点和最低点,设为变量。再定义一个正向移动的标志,乘在vector3的后面(控制移动的方向)。

if(this.transform.position.y>FloYPositionmax)
        {
            UpMovingFlag = -1;
        }
        else if(this.transform.position.y<FloYPositionmin)
        {
            UpMovingFlag = 1;
        }
        this.transform.Translate(new Vector3(0,1,0) * MovingSpeed*UpMovingFlag
            ,Space.World);

然后把之前那个碰撞检测结束游戏的脚本绑在对应的刺上。将做好的物体设为预制体,方便之后的动态生成。注意脚本组件挂上相应的GameObject

(二)对障碍物树干的改动

来实现一个触发器控制树从空中落下来的效果,增加游戏难度。方法与之前的大同小异,只不过这回Add的是RigidBody组件。同样,写一个触发器上的脚本TriggerFalling:

public class DynamicTriggerFalling : MonoBehaviour {
    public GameObject FallingObject;
 // Use this for initialization
void Start () {
 
 }
private void OnTriggerEnter(Collider col)
 {
     FallingObject.AddComponent<ObjectFalling>();
 }
    // Update is called once per frame
    void Update () {
    }

然后是添加刚体组件的脚本主要代码,注意初始化一下刚体的一些属性,用到了GetComponent方法:

private Rigidbody rb1;
 // Use this for initialization
 void Start () {
        this.gameObject.AddComponent<Rigidbody>();
        rb1 = this.gameObject.GetComponent<Rigidbody>();
        rb1.mass = 5;
        rb1.useGravity = true;
    }

在这里插入图片描述
在这里插入图片描述
障碍物的设置就到这里,之后可以再增加一些特性,接下来来处理角色的动画。

二.角色动作的优化

目前角色的动画都放在HeroController中的,完全由Animation.play()来播放,这样子在加上跳跃,滑铲等动作后会出很大的问题,按下按键后动作会直接切换(不管播没播放结束),十分生硬。为了解决这个问题,需要重新写一下Controller中的内容,使用协程来控制人物动作。当然,这里也可以用动画状态机(animator)来实现,以后有时间专门来复习一下。
首先,在GlobalManager中新加入一个枚举变量,来指示人物的动作

public enum HeroActionType {None,Run,Jump,Bend,Fall}
//initialize
//7.28 action types
public static HeroActionType ActionType = HeroActionType.None;

然后打开HeroController脚本,把之前写的控制角色左右移动以及动画播放的代码全部注释掉,添加两个协程,一个用来接收用户输入,控制角色移动,一个用来控制对应的动作。这里跳跃动作用到了RigidBody的AddForce给物体添加一个冲力的方法来实现。

IEnumerator GetHeroActionInput()
    {
        yield return new WaitForEndOfFrame();
        while(true)
        {
            yield return new WaitForSeconds(0.01f);
            if(GlobalManager.GlobalState==GameState.Playing)
            {
                //Default setting:running
                GlobalManager.ActionType = HeroActionType.Run;
                //Turning
                if(Input.GetKey(KeyCode.A))
                {
                    this.transform.Rotate(Vector3.down * FloRotateSpeed);
                }
                else if (Input.GetKey(KeyCode.D))
                {
                    this.transform.Rotate(Vector3.up * FloRotateSpeed);
                }
                //Jumping
                else if(Input.GetKeyDown(KeyCode.Space))
                {
                    HeroRB.AddForce(Vector3.up * HeroJumpPower, ForceMode.Impulse);
                    GlobalManager.ActionType = HeroActionType.Jump;
                    yield return new WaitForSeconds(AmimeJump.length);
                }
                else if(Input.GetKeyDown(KeyCode.S))
                {
                }
              }
            }
          }

IEnumerator PlayHeroAnimation()
    {
        yield return new WaitForEndOfFrame();
        while(true)
        {
            yield return new WaitForSeconds(0.01f);
            if (GlobalManager.GlobalState==GameState.Playing)
            {
                switch(GlobalManager.ActionType)
                {
                    case HeroActionType.None:
                        break;
                    case HeroActionType.Run:
                        anime.Play(AnimeRun.name);
                        yield return new WaitForSeconds(AnimeRun.length);
                        break;
                    case HeroActionType.Jump:
                        anime.Play(AmimeJump.name);
                        yield return new WaitForSeconds(AmimeJump.length);
                        break;
                    case HeroActionType.Bend:
                        break;
                    case HeroActionType.Fall:
                        break;
                }
            }
        }
    }

最好还是用switch来处理枚举变量。这里我们用到了AnimationClip,即剪辑得来的动画片段,我们把它的时间长度length来作为yield return的秒数,这样就不会出现一个动作还没有做完就强行开始进行下一个动作了。注意定义好animationClip,并且在脚本组件绑上对象。
在这里插入图片描述
GlobalManager和HeroController脚本变动很大,这里粘贴一下:
GlobalManager.cs

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

//enum description:
//none:no sounds
//halfvolume:halfvolume
//fullvolume:fullvolume
public enum ProjectVolume { none,FullVolume,HalfVolume,NoneVolume}

//enunm description:
//none:wait for a state
//preparing:counting down coroutine
//playing:normal game
//gameover:gameover
//pause:pause (game stop)
public enum GameState {none,Preparing,Playing,GameOver,Pause}
public enum HeroActionType {None,Run,Jump,Bend,Fall}

public class GlobalManager : MonoBehaviour {
//volume controller
    public static ProjectVolume GlobalVol = ProjectVolume.FullVolume;

    //game state controller
    public static GameState GlobalState = GameState.none;
    
    //count RedDiamonds
    public static int RedDiamondCount=0;
    
    //count Distance
    public static int RunningDistance = 0;
    
    //7.28 action types
    public static HeroActionType ActionType = HeroActionType.None;
    }
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class Hero_Control_PC : MonoBehaviour {
   // public GameObject GoHero;
    public float FloRunningSpeed=1f;
    public float FloRotateSpeed = 1f;
    public float HeroJumpPower = 1f;
    //7.28 Animation Clips
    public AnimationClip AnimeRun;
    public AnimationClip AmimeJump;
    public AnimationClip AmimeBend;
    public AnimationClip AmimeFall;
    private Animation anime;
    private Rigidbody HeroRB;
 // Use this for initialization
 void Start () {
        anime = this.GetComponent<Animation>();
        HeroRB = this.GetComponent<Rigidbody>();
        //7.28 Get Key Input
        StartCoroutine("GetHeroActionInput");
        //play Hero's Animation
        StartCoroutine("PlayHeroAnimation");
 }
 IEnumerator GetHeroActionInput()
    {
        yield return new WaitForEndOfFrame();
        while(true)
        {
            yield return new WaitForSeconds(0.01f);
            if(GlobalManager.GlobalState==GameState.Playing)
            {
                //Default setting:running
                GlobalManager.ActionType = HeroActionType.Run;
                //Turning
                if(Input.GetKey(KeyCode.A))
                {
                    this.transform.Rotate(Vector3.down * FloRotateSpeed);
                }
                else if (Input.GetKey(KeyCode.D))
                {
                    this.transform.Rotate(Vector3.up * FloRotateSpeed);
                }
                //Jumping
                else if(Input.GetKeyDown(KeyCode.Space))
                {
                    HeroRB.AddForce(Vector3.up * HeroJumpPower, ForceMode.Impulse);
                    GlobalManager.ActionType = HeroActionType.Jump;
                    yield return new WaitForSeconds(AmimeJump.length);
                }
                else if(Input.GetKeyDown(KeyCode.S))
                {
                }
             }
          }
       }
IEnumerator PlayHeroAnimation()
    {
        yield return new WaitForEndOfFrame();
        while(true)
        {
            yield return new WaitForSeconds(0.01f);
            if (GlobalManager.GlobalState==GameState.Playing)
            {
                switch(GlobalManager.ActionType)
                {
                    case HeroActionType.None:
                        break;
                    case HeroActionType.Run:
                        anime.Play(AnimeRun.name);
                        yield return new WaitForSeconds(AnimeRun.length);
                        break;
                    case HeroActionType.Jump:
                        anime.Play(AmimeJump.name);
                        yield return new WaitForSeconds(AmimeJump.length);
                        break;
                    case HeroActionType.Bend:
                        break;
                    case HeroActionType.Fall:
                        break;
                }
            }
        }
    }
 // Update is called once per frame
 void Update () {
        //7.24 latest updata
         if(GlobalManager.GlobalState==GameState.Playing)
        {
            this.transform.Translate(Vector3.forward * FloRunningSpeed);
        }
     }
  }
  • 1
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值