基于Unity3d,MagicalVoxel建模的0成本像素风的寝室逃脱游戏(BTH010游戏开发期末实验)

基于Unity3d,MagicalVoxel建模的0成本像素风的寝室逃脱游戏

目录导航:

  1. 总体介绍
  2. 游戏功能及建模介绍
  3. 核心代码
1. 总体介绍

该游戏是基于MagicalVoxel(类似于我的世界的像素模型)的模型,利用免费www.mixamo.com给静态模型添加动画,在Unity3D中完成的小型像素可爱风的解谜游戏——逃离寝室游戏
本人与室友合作完成制作周期为7天,制作成本由于MagicalVoxel与mixamo的免费而达到0。

2. 游戏功能及建模介绍

主页及游戏界面:

五种结局(完成会解锁):
基于Unity3d,MagicalVoxel建模的0成本像素风的寝室逃脱游戏(BTH010游戏开发期末实验)_第1张图片
一些建模(还原自己学校寝室,略丑勿怪):

基于Unity3d,MagicalVoxel建模的0成本像素风的寝室逃脱游戏(BTH010游戏开发期末实验)_第2张图片 基于Unity3d,MagicalVoxel建模的0成本像素风的寝室逃脱游戏(BTH010游戏开发期末实验)_第3张图片 基于Unity3d,MagicalVoxel建模的0成本像素风的寝室逃脱游戏(BTH010游戏开发期末实验)_第4张图片
3. 核心代码

代码部分主要分:相机控制代码,人物移动跳跃代码,事件触发及UI设计,实例结构四块来陈述,在最后将附上源码文件,望大家能参考并指正错误。

视角代码

代码包括:第三人称相机跟随镜头旋转,缩放,实现遇到障碍:有物体透明化或拉近镜头的功能

这部分代码参考于另以为博主(由于时间久远具体不知)的第三人称相机设置,若相机与player间有物体将会自动拉近相机(部分被注释掉代码实现的功能),后因我的室友发现掉落时出现bug而修改为物体半透明化,在此我搜索了部分资料重新修改并做了更详细的注解。


public class CameraController : MonoBehaviour
{
    public GameObject player;

    //初始观察距离
    public float Distance = 1F;
    //旋转速度
    public float SpeedX = 240;
    public float SpeedY = 120;
    //角度限制
    public float MinLimitY = 5;
    public float MaxLimitY = 180;

    //旋转角度
    private float mX = 0.0F;
    private float mY = 0.0F;

    //鼠标缩放距离最值
    public float MaxDistance = 3;
    public float MinDistance = 0.2F;
    //鼠标缩放速率
    private float ZoomSpeed = 2F;
    //速度
    public float Damping = 10F;

    private Quaternion mRotation;
    private List<GameObject> collideredObjects;//本次射线hit到的GameObject
    private List<GameObject> bufferOfCollideredObjects;//上次射线hit到的GameObject

    void Start()
    {
        mX = transform.eulerAngles.x;
        mY = transform.eulerAngles.y;
        collideredObjects = new List<GameObject>();
        bufferOfCollideredObjects = new List<GameObject>();
    }


    void LateUpdate()
    {
        CameraSet();//相机设置
        //定义一条射线
        /*
        RaycastHit hit;
        if (Physics.Linecast(player.transform.position, this.transform.position, out hit))
        {
            GameObject item = hit.collider.gameObject;
            string name = item.name;
            if (name != "Main Camera")
            {
                //如果射线碰撞的不是相机,那么就取得射线碰撞点到玩家的距离
                float currentDistance = Vector3.Distance(hit.point, player.transform.position);
                //如果射线碰撞点小于玩家与相机本来的距离,就说明角色身后是有东西,为了避免穿墙,就把相机拉近
                
                if (currentDistance < m_distanceAway)
                {
                    this.transform.position = hit.point;
                }
            }
        }*/

        //清除上一次碰撞的对象
        bufferOfCollideredObjects.Clear();
        //循环将本次碰撞的对象添加到上次碰撞对象list中
        for (int temp = 0; temp < collideredObjects.Count; temp++)
        {
            bufferOfCollideredObjects.Add(collideredObjects[temp]);//得到上次的
        }
        //清除本次
        collideredObjects.Clear();

        //发射射线
        //dir是玩家到camera的方向
        Vector3 dir = -(player.transform.position - transform.position).normalized;
        RaycastHit[] hits;
        //意思是将从玩家出发以dir方向射出Vector3.Distance(player.transform.position, transform.position)距离的射线,将碰撞的物体添加到hits中
        hits = Physics.RaycastAll(player.transform.position, dir, Vector3.Distance(player.transform.position, transform.position));
        //将碰撞对象添加到本次碰撞list中
        for (int i = 0; i < hits.Length; i++)
        {
            if (hits[i].collider.gameObject.name != "Plane" && hits[i].collider.gameObject.name != "Cube")
            {
                collideredObjects.Add(hits[i].collider.gameObject);//得到现在的
            }
        }
        //把上次的还原,这次的透明
        for (int i = 0; i < bufferOfCollideredObjects.Count; i++)
        {
            SetMaterialsColor(bufferOfCollideredObjects[i].GetComponent<Renderer>(), false);
        }
        for (int i = 0; i < collideredObjects.Count; i++)
        {
            SetMaterialsColor(collideredObjects[i].GetComponent<Renderer>(), true);
        }
    }

    //是否搞透明
    void SetMaterialsColor(Renderer r, bool isClear)
    {
        if (isClear)
        {
            //获得对象r的所有材质列表的数量
            int materialsNumber = r.sharedMaterials.Length;
            for (int i = 0; i < materialsNumber; i++)
            {
                //将所有材质修改为Transparent / Diffuse,并修改颜色属性1为完全不透明与255等价,0为完全透明,这里的0.4为半透明
                r.materials[i].shader = Shader.Find("Transparent/Diffuse");
                Color tempColor = r.materials[i].color;
                tempColor.a = 0.4f;
                r.materials[i].color = tempColor;

            }
        }
        else
        {
            //同理
            int materialsNumber = r.sharedMaterials.Length;
            for (int i = 0; i < materialsNumber; i++)
            {
                r.materials[i].shader = Shader.Find("Legacy Shaders/Diffuse");
                Color tempColor = r.materials[i].color;
                tempColor.a = 1f;
                r.materials[i].color = tempColor;

            }
        }

    }

    void CameraSet()
    {
        mX += Input.GetAxis("Mouse X") * SpeedX * 0.02F;
        mY -= Input.GetAxis("Mouse Y") * SpeedY * 0.02F;
        //范围限制
        mY = ClampAngle(mY, MinLimitY, MaxLimitY);
        //计算旋转
        //我们可以通过Quaternion.Euler()方法将一个Vector3类型的值转化为一个四元数, 进而通过修改Transform.Rotation来实现相同的目的
        mRotation = Quaternion.Euler(mY, mX, 0);
        transform.rotation = Quaternion.Lerp(transform.rotation, mRotation, Time.deltaTime * Damping);

        //鼠标滚轮缩放
        Distance -= Input.GetAxis("Mouse ScrollWheel") * ZoomSpeed;
        Distance = Mathf.Clamp(Distance, MinDistance, MaxDistance);

        //重新计算位置
        Vector3 mPosition = mRotation * new Vector3(0.0F, 0.0F, -Distance) + player.transform.position;
        transform.position = Vector3.Lerp(transform.position, mPosition, Time.deltaTime * Damping);
    }
    //角度范围限制
    private float ClampAngle(float angle, float min, float max)
    {
        if (angle < -360) angle += 360;
        if (angle > 360) angle -= 360;
        return Mathf.Clamp(angle, min, max);
    }
}
移动跳跃代码

代码包括:跳跃判定,玩家跟随镜头旋转,角色移动,已详细注解

// Update is called once per frame
    void Update()
    {
        //时刻受重力作用
        moveDirection.y -= gravity * Time.deltaTime;

        controller.Move(moveDirection * Time.deltaTime);

        moveH = Input.GetAxis("Horizontal");
        moveV = Input.GetAxis("Vertical");
        
        if (gameController.gamePaused)
        {
            moveH = 0;
            moveV = 0;
        }
        //判断玩家是否在地上,系统自带isGrounded
        if (controller.isGrounded)
        {
            moveDirection.y = 0;
            if (Input.GetButtonDown("Jump"))
            {
                moveDirection.y = jumpSpeed;
                GameObject tmp = Instantiate(jumpAudio);
                Destroy(tmp, 1);
            }
        }
        controller.Move(moveDirection * Time.deltaTime);
        //角色模型方向跟随相机转动,sight是传入的相机对象,判断两者方向是否一致
        if (Mathf.Abs(transform.rotation.y) != Mathf.Abs(sight.transform.rotation.y))
        {
            transform.eulerAngles = new Vector3(0, sight.transform.eulerAngles.y, 0);
        }
        //利用系统提供的人物模型类CharacterController移动,在start()中controller = this.GetComponent()
        controller.Move(moveH * new Vector3(transform.right.x, 0, transform.right.z) * speed);
        controller.Move(moveV * new Vector3(transform.forward.x, 0, transform.forward.z) * speed);
        //UI设置
        if (Input.GetMouseButtonDown(1) && UI.canRelease)
        {
            Item item = playerInformation.carryItem.GetComponent<Item>();
            Release(item);
            UI.getItem(item);
            UI.showItem(item);
        }
    }
触发代码及UI设计

代码包括:捡起释放物体(固定物体与player一起移动),事件触发状态判定

    private void PickUp(GameObject MeetItem)
    {
        //判断状态是否已经捡起物体
        if (!playerInformation.isCarryItem)
        {
            UI.carryState = true;
            //获得遇到物体中的组件item(每个可捡起的物体都有一个组件Script,包含是否被捡起及UI信息,下文图片实例)
            Item item = MeetItem.GetComponent<Item>();
            //调整方向
            gameObject.transform.LookAt(MeetItem.transform.Find("MenuBox"));
            //设置将要捡起的物体的父对象为player,并且用GetComponent().constraints冻结物体的位移,并更新状态
            MeetItem.transform.SetParent(gameObject.transform);
            playerInformation.isCarryItem = true;
            playerInformation.carryItem = MeetItem;
            item.isCarryed = true;
            MeetItem.GetComponent<Rigidbody>().constraints = RigidbodyConstraints.FreezeAll;
            UI.hideAll();
        }

    }

    private void Release(Item item)
    {
        //同理,捡起反之
        item.GetComponent<Rigidbody>().constraints = RigidbodyConstraints.None;
        UI.carryState = false;
        item.transform.parent = null;
        UI.hideAll();
        playerInformation.isCarryItem = false;
        playerInformation.carryItem = null;
        item.isCarryed = false;
    }

    public void MeetEvent(GameObject MeetItem)
    {
        //获得遇到物体中的组件item,同上
        Item item = MeetItem.GetComponent<Item>();
        UI.getItem(item);
        UI.showAll();
        //一系列的状态判断
        if (playerInformation.isCarryItem)
        {
            Item carryItem = playerInformation.carryItem.GetComponent<Item>();
            if (carryItem.itemName == "枕头")
            {
                if (item.itemName == "下方")
                {
                    UI.changeBehaviour2("扔下去");
                }
            }
            //此处省略很多并列判断
        }
        else
        {
            if (item.itemName == "寝室门")
            {
                if (powerful)
                {
                    UI.changeBehaviour2("踢飞");
                    item.description = "我感觉我能把门踢飞!";
                }
                if (big)
                {
                    UI.changeBehaviour2("打开");
                    item.description = "现在我可以直接开门了";
                }
            }
        }
    }
    //右键点击触发事件同理
    public void RightClickEvent(GameObject MeetItem)
    {
        Item item = MeetItem.GetComponent<Item>();
        if (playerInformation.isCarryItem)
        {
            Item carryItem = playerInformation.carryItem.GetComponent<Item>();
            if (carryItem.itemName == "枕头")
            {
                if (item.itemName == "下方")
                {
                    Release(carryItem);
                    carryItem.isCarryed = true;
                    pillowNew.SetActive(true);
                    GameObject.Find("Pillow").SetActive(false);
                }
            }
            //此处省略很多并列判断
            }
        }
   }

每个可捡起对象中的组件Item:
基于Unity3d,MagicalVoxel建模的0成本像素风的寝室逃脱游戏(BTH010游戏开发期末实验)_第5张图片

对象实例结构

Player结构
Model:Animator controller
stepOn: 用于判断高度及是否player站在其他物体上
基于Unity3d,MagicalVoxel建模的0成本像素风的寝室逃脱游戏(BTH010游戏开发期末实验)_第6张图片
基于Unity3d,MagicalVoxel建模的0成本像素风的寝室逃脱游戏(BTH010游戏开发期末实验)_第7张图片
举例:
Trash:一个可触发的物体,小的碰撞范围检查碰撞
MenuBox:大的碰撞范围,检测player靠近触发UI提示
在这里插入图片描述
基于Unity3d,MagicalVoxel建模的0成本像素风的寝室逃脱游戏(BTH010游戏开发期末实验)_第8张图片
源码及游戏,github链接:
https://github.com/923076046/Escape-from-the-dormitory-game.git

你可能感兴趣的:(MagicalVoxel,像素,游戏开发作业)