unity3D游戏开发实战(一)——角色移动与对白

游戏理念

欲制作的游戏为一款3D解密游戏,通过剥夺玩家的视觉,来增强听觉的重要性,让玩家在黑暗中依据声音进行探索,并且随着探索点亮部分地区,最终让玩家解开谜题。

游戏底层构建

游戏的实现本身并不困难,没有涉及到太多技术层面的问题,重点在于关卡的编排和设计,因此我使用了unity自带的默认3D物体来代替各种物品,并写了简单的控制操作脚本,代码如下:

public class MoveController : MonoBehaviour
{
    public Camera sight;
    public Light light;
    public float rotateSpeed_x;
    public float rotateSpeed_y;
    public float jumpSpan;
    public float moveSpeed;
    
    private Rigidbody selfBody;
    private bool jumping = false;

    // Start is called before the first frame update
    void Start()
    {
        selfBody = gameObject.GetComponent<Rigidbody>();
    }

    // Update is called once per frame
    void Update()
    {
        selfBody.angularVelocity = new Vector3(0, 0, 0);
        var mouse_x = Input.GetAxis("Mouse X");
        var mouse_y = -Input.GetAxis("Mouse Y");

        transform.Rotate(0, mouse_x * rotateSpeed_x * Time.deltaTime, 0);

        if (Input.GetKey(KeyCode.W))
        {
            if (!Input.GetKey(KeyCode.S))
                transform.Translate(0, 0, moveSpeed * Time.deltaTime);
        }
        else if (Input.GetKey(KeyCode.S))
            transform.Translate(0, 0, -moveSpeed * Time.deltaTime);
        if (Input.GetKey(KeyCode.A))
        {
            if (!Input.GetKey(KeyCode.D))
                transform.Translate(-moveSpeed * Time.deltaTime, 0, 0);
        }
        else if (Input.GetKey(KeyCode.D))
            transform.Translate(moveSpeed * Time.deltaTime, 0, 0);

        if (Input.GetKeyDown(KeyCode.Space)&&!jumping)
        {
            gameObject.GetComponent<Rigidbody>().AddForce(new Vector3(0, 6.5f, 0), ForceMode.Impulse);
            jumping = true;
            Invoke("JumpAgain", jumpSpan);
        }

        sight.transform.rotation = Quaternion.Euler(sight.transform.rotation.eulerAngles.x,transform.rotation.eulerAngles.y,transform.rotation.eulerAngles.z);
        sight.transform.position = transform.position;
        light.transform.position = transform.position;

        sight.transform.Rotate(mouse_y * rotateSpeed_y * Time.deltaTime, 0, 0);
    }

    private void OnCollisionEnter(Collision collision)
    {
        if (collision.transform.CompareTag("Untouched"))
        {
            collision.transform.tag = "Touched";
            Instantiate<Light>(light, collision.transform.position, collision.transform.rotation, collision.transform);
        }
    }

    private void JumpAgain()
    {
        jumping = false;
    }
}

灯光渐变代码:

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

public class LightShow : MonoBehaviour
{
    public float targetIntensity;
    public float intensityChangeSpeed;

    private Light light;

    void Start()
    {
        light = GetComponent<Light>();
    }

    void Update()
    {
        if (light.intensity < targetIntensity)
        {
            light.intensity += intensityChangeSpeed * Time.deltaTime;
        }
        else
            this.enabled = false;
    }
}

挂载给对应的物体生成预制体后,最简单的游戏框架就写好了,关于和场景物体的互动方式、游戏初始界面UI、游戏存档以及音频变化等代码我会在后期完善,接下来就是关卡的粗略设计。

初始房间设计

玩家在刚出生的时候对于游戏还一无所知,手中的资料仅仅只有游戏说明的大致介绍与粗略的操作方式,因此为了让玩家快速上手游戏,在游戏初期要给予引导,因此第一个房间一定要简单明了,空旷的房间内只有一个待操作物体与一扇门,将要操作的物体放在玩家的正前方,让玩家对于如何操作一目了然。
初始房间雏形
然而在玩家刚刚进入游戏时,除了眼前黑漆漆的一切之外什么都看不到,最好给予对话引导,因此我们来编写一下旁白的脚本。

旁白脚本

在场景中新建Canvas,命名为Dialog,将其绑定在MainCamera上,在其下方新建一个Text区域命名为DialogText,调整字体及位置后为其撰写出现以及消失的脚本。

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

public class DialogShow : MonoBehaviour
{
    public float fadeTime;
    public float showTime;
    public float continueTime;

    private Text text;
    private float fadeSpeed;
    private float showSpeed;
    private float instantiateTime;
    private bool showing = false;
    private bool spanning = false;
    private bool disappearing = false;
    private Queue<string> dialogs = new Queue<string>();
    void Start()
    {
        text = GetComponent<Text>();
        fadeSpeed = 1 / fadeTime;
        showSpeed = 1 / showTime;
        instantiateTime = Time.time;
    }

    void Update()
    {
        if (spanning && dialogs.Count > 0)
        {
            spanning = false;
            text.text = dialogs.Dequeue();
            showing = true;
            instantiateTime = Time.time;
        }

        if (showing)
        {
            text.color = new Color(0, 0, 0, showSpeed * Time.deltaTime) + text.color;
            if (text.color.a >= 1f)
                showing = false;
        }

        if (Time.time - instantiateTime >= continueTime)
            disappearing = true;

        if (disappearing)
        {
            text.color = new Color(0, 0, 0, -fadeSpeed * Time.deltaTime) + text.color;
            if (text.color.a <= 0)
            {
                disappearing = false;
                spanning = true;
            }
        }
    }

    void ChangeContinueTime(float time)
    {
        continueTime = time;
    }

    void ShowDialog(string s)
    {
        dialogs.Enqueue(s);
    }
}


在其他的脚本中,只要向DialogText物体使用SendMessage函数调用ShowDialog就可以显示对话,要改变持续时间则可以调用ChangeContinueTime,spanning则是用来检测当前是否有其他对话正在进行,来让触发对话的物体进行检测,并用队列储存对话,来检测当前是否有未显示的对话。
之后则是编写触发文本的脚本了,我们可以提前设想会有几种触发方式:
1、与物体交互后触发
2、走到对应区域触发
3、一段时间后触发
大致就是以上三种触发方式,因此我们要编写对应的脚本,新建三个脚本文件,分别是AreaDialog、ContactDialog与TimeDialog,对应脚本如下:

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

public class AreaDialog : MonoBehaviour
{
    public GameObject dialogPrefeb;
    public string dialog;

    private void OnTriggerEnter(Collider other)
    {
        if (other.transform.CompareTag("Character"))
        {
            dialogPrefeb.SendMessage("ShowDialog", dialog);
            Destroy(gameObject);
        }
    }
}


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

public class ContactDialog : MonoBehaviour
{
    public GameObject dialogPrefeb;
    public string dialog;

    private void OnCollisionEnter(Collision collision)
    {
        if (collision.transform.CompareTag("Character"))
        {
            dialogPrefeb.SendMessage("ShowDialog", dialog);
            enabled = false;
        }
    }
}

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

public class TimeDialog : MonoBehaviour
{
    public float delayShowTime;
    public GameObject dialogPrefeb;
    public string dialog;

    private float insTime;

    private void Start()
    {
        insTime = Time.time;
    }

    private void Update()
    {
        if (Time.time - insTime >= delayShowTime)
        {
            dialogPrefeb.SendMessage("ShowDialog", dialog);
            enabled = false;
        }
    }
}

其中互动触发和区域触发代码十分相像,是因为目前我们还没有写互动代码,互动方式暂且为触碰,因此ContactDialog日后需要修改。
区域触发需要在unity中将collider组件勾选isTrigger选框,并且监测区域只需要使用一次,因此可以直接使用Destroy来节省空间。
时间触发类多半会在某个对话触发后才触发,因此其需要有触发条件,但是我们的项目暂时还用不到时间类触发对话,因此暂时忽略它。
养成良好的编程习惯,将有关对话显示的脚本归类在一个Dialog的文件夹下。
归类
ok,到这里我们的基础准备工作也做的差不多了,在第一个场景中加入些许对话触发,初始房间就制作完成了,让我们看看效果。
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
游戏的初步制作就到这里,下一次我将会制作关卡具体内容并引导玩家,谢谢大家的阅读。

  • 4
    点赞
  • 37
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值