• 【Unity】3D贪吃蛇游戏制作/WebGL本地测试及项目部署


    本文是Unity3D贪吃蛇游戏从制作到部署的相关细节
    项目开源代码:https://github.com/zstar1003/3D_Snake
    试玩链接:http://xdxsb.top/Snake_Game_3D
    效果预览:
    在这里插入图片描述
    试玩链接中的内容会和该效果图略有不同,后面会详细说明。

    游戏规则

    经典贪吃蛇游戏:蛇身随着吃食物的增加不断变长,通过A/D或方向键←→控制方向,蛇头撞在蛇身上或四周墙壁会导致游戏失败。

    蛇身控制和碰撞检测

    蛇身控制和碰撞检测的逻辑写在SnakeController.cs文件中。

    蛇头运动的思路是将蛇头不断朝forward的方向前进,前进速度等于速度数值x当前时间。同时通过一个list来记录蛇头运动的历史轨迹,蛇身通过该轨迹进行运动。

    为了区分延申出来的蛇身是初始蛇身还是新延申的蛇身,对新延申的蛇身打上Block标签,不进行区分则会导致刚开始碰撞即触发蛇头蛇身碰撞,导致游戏结束。

    完整代码:

    using System;
    using System.Collections;
    using System.Collections.Generic;
    using System.Linq;
    using UnityEngine;
    using UnityEngine.SceneManagement;
    
    public class SnakeController : MonoBehaviour
    {
        // 设置
        public float moveSpeed = 5f;
        public float steerSpeed = 180f;
        public float bodySpeed = 5f;
        public int Gap = 10;
    
        // 预制体
        public GameObject bodyPrefab;  //身体组件
    
        // 身体组件集合
        private List<GameObject> _bodyParts = new List<GameObject>();
        private List<Vector3> _positionHistory = new List<Vector3>();
    
        //音乐控制器
        public AudioController audioController; 
    
        private void Start()
        {
            addBodyPart();
            audioController = GameObject.FindGameObjectWithTag("Audio").GetComponent<AudioController>();
        }
        private void Update()
        {
            // 向前移动
            transform.position += transform.forward * moveSpeed * Time.deltaTime;
    
            // 方向操控
            float steerDirection = Input.GetAxis("Horizontal");  // 返回值从 -1 到 1
            transform.Rotate(Vector3.up * steerDirection * steerSpeed * Time.deltaTime);
    
            // 保存位置移动史
            _positionHistory.Insert(0, transform.position);
    
            // 移动身体组件
            int index = 0;
            foreach (var body in _bodyParts)
            {
                Vector3 point = _positionHistory[Mathf.Clamp(index * Gap, 0, _positionHistory.Count - 1)];
    
                // 让贪吃蛇的身体组件沿着头部的移动轨迹运动
                Vector3 moveDirection = point - body.transform.position;
                body.transform.position += moveDirection * bodySpeed * Time.deltaTime;
    
                // 让身体组件朝向头部移动的方向 
                body.transform.LookAt(point);
    
                index++;
            }
        }
           
        // 蛇身延长
        private void addBodyPart()
        {
            GameObject body = Instantiate(bodyPrefab, new Vector3(0, transform.position.y, 0), Quaternion.identity);
            _bodyParts.Add(body);
        }
    
        // 后续添加的body打上Block标签
        private void addBodyPart_Block()
        {
            GameObject body = Instantiate(bodyPrefab, new Vector3(0, _bodyParts.Last().transform.position.y, 0), Quaternion.identity);
            body.tag = "Block";
            _bodyParts.Add(body);
        }
    
    
        //触发检测
        private void OnTriggerEnter(Collider other)
        {
            if (other.tag == "Food")
            {   
                //必须先删除,否则会导致多次触发
                Destroy(other.gameObject);
                addBodyPart_Block();
                GameObject.Find("SpawnPoint").GetComponent<SpawnItem>().SpawnItems();
                audioController.PlaySfx(audioController.eat);
            }
            else if (other.tag == "Block")
            {
                SceneManager.LoadScene(SceneManager.GetActiveScene().buildIndex + 1);
            }
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92

    食物旋转

    控制食物旋转比较简单,在update中加入Rotate即可。

    Food.cs

    using System.Collections;
    using System.Collections.Generic;
    using UnityEngine;
    
    public class Food : MonoBehaviour
    {
        void Start()
        {
    
        }
        void Update()
        {
            //旋转
            transform.Rotate(Vector3.up);
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    食物随机生成

    食物随机生成我并没有采用随机数的方式,三维场景容易出现问题。因此这里在场景中添加了6个食物生成的点位,当食物被触发之后,在随机的一个点位上生成新的食物。

    SpawnItem.cs

    using System.Collections;
    using System.Collections.Generic;
    using UnityEngine;
    
    public class SpawnItem : MonoBehaviour
    {
        public Transform[] SpawnPoints;
        public float spawnTime = 2.5f;
        public GameObject Items;
        void Start()
        {
    
        }
    
        void Update()
        {
            
        }
    
        public void SpawnItems()
        {
            int spawnIndex = Random.Range(0, SpawnPoints.Length);
            Instantiate(Items, SpawnPoints[spawnIndex].position, SpawnPoints[spawnIndex].rotation);
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25

    在这里插入图片描述

    场景切换

    这里对于游戏开始界面和结束界面分别用不同的场景进行隔离,切换时只需一行代码:

    SceneManager.LoadScene(SceneManager.GetActiveScene().buildIndex + 1);
    
    • 1

    这里的Index为打包时场景的序号顺序。

    本地WebGL测试

    使用WebGL打包之后,会得到3个文件夹和一个index.html文件,直接打开index.html会报错,需要使用服务器方式去运行。

    首先在win10上配置服务器相关组件,参考之前的博文【实用技巧】Win10搭建局域网FTP服务器

    之后在打包的文件夹下新建一个文件web.config,输入以下内容:

    <?xml version="1.0" encoding="utf-8"?>
    <!--
      有关如何配置 ASP.NET 应用程序的详细信息,请访问
      https://go.microsoft.com/fwlink/?LinkId=169433
    -->
    <configuration>  
      <system.webServer>
        <httpProtocol>
          <!-- 允许跨域配置 -->
          <customHeaders>
            <add name="Access-Control-Allow-Origin" value="*" />
            <add name="Access-Control-Allow-Headers" value="X-Requested-With,Content-Type,Authorization" />
            <add name="Access-Control-Allow-Methods" value="GET, POST, PUT, DELETE,OPTIONS" />
            <add name="Access-Control-Allow-Credentials" value="true" />
          </customHeaders>
        </httpProtocol>
            <staticContent>
                <remove fileExtension=".mem" />  
                <remove fileExtension=".data" />  
                <remove fileExtension=".unity3d" />  
                <remove fileExtension=".jsbr" />  
                <remove fileExtension=".membr" />  
                <remove fileExtension=".databr" />  
                <remove fileExtension=".unity3dbr" />  
                <remove fileExtension=".jsgz" />  
                <remove fileExtension=".memgz" />  
                <remove fileExtension=".datagz" />  
                <remove fileExtension=".unity3dgz" />  
                <remove fileExtension=".json" />  
                <remove fileExtension=".unityweb" />  
               
                <mimeMap fileExtension=".mem" mimeType="application/octet-stream" />  
                <mimeMap fileExtension=".data" mimeType="application/octet-stream" />  
                <mimeMap fileExtension=".unity3d" mimeType="application/octet-stream" />  
                <mimeMap fileExtension=".jsbr" mimeType="application/octet-stream" />  
                <mimeMap fileExtension=".membr" mimeType="application/octet-stream" />  
                <mimeMap fileExtension=".databr" mimeType="application/octet-stream" />  
                <mimeMap fileExtension=".unity3dbr" mimeType="application/octet-stream" />  
                <mimeMap fileExtension=".jsgz" mimeType="application/x-javascript; charset=UTF-8" />  
                <mimeMap fileExtension=".memgz" mimeType="application/octet-stream" />  
                <mimeMap fileExtension=".datagz" mimeType="application/octet-stream" />  
                <mimeMap fileExtension=".unity3dgz" mimeType="application/octet-stream" />  
                <mimeMap fileExtension=".json" mimeType="application/json; charset=UTF-8" />  
                <mimeMap fileExtension=".unityweb" mimeType="application/octet-stream" />  
            </staticContent>
      </system.webServer>
    </configuration>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47

    之后在iis中,新建一个http服务器,选择一个不被占用的端口,我这里选择8080端口。
    在这里插入图片描述

    开启网站后,在浏览器输入http://localhost:8080/,即可访问测试。
    在这里插入图片描述

    Github部署

    Github部署非常容易,新建一个仓库,将打包出的内容直接上传。

    在这里插入图片描述
    然后在Settings/Pages选择main分支,点击Save,过几分钟就会在上方出现访问网址。
    在这里插入图片描述

    遗留问题:打包前后测试不一致

    目前该项目在untiy运行测试时正常, 但打包出webgl或exe时,却出现蛇身分离的情况,看了一些打包时的选项,仍未解决该问题,有了解这一问题的读者欢迎在评论区交流。

  • 相关阅读:
    2009年408大题总结
    【JDK】Comparable与Comparator通俗易懂版
    2024年【R1快开门式压力容器操作】模拟考试题及R1快开门式压力容器操作复审模拟考试
    python django初步搭建(一)
    Flink(二)
    多大的串扰算大?
    万字好文:大报文问题实战
    刷爆力扣之第三大的数
    计算机体系结构:按写分配法、不按写分配法例题
    QT Day4
  • 原文地址:https://blog.csdn.net/qq1198768105/article/details/133518647