Unity3D 小案例 像素贪吃蛇 01 蛇的移动

news2024/11/16 14:22:45

Unity3D 小案例 像素贪吃蛇 第一期 蛇的移动

像素贪吃蛇

今天来简单制作一个小案例,经典的像素贪吃蛇。

准备

首先调整一下相机的设置,这里使用灰色的纯色背景,正交视图。

相机设置

接着,创建一个正方形,保存为预制体,一个蛇头,一个蛇身。蛇身稍微调成灰色。

创建预制体

初始生成

创建脚本 Snake.cs,挂到 Snake 预制体上。

这里先定义初始身体数量,身体预制体,身体列表。

在 Start 方法里,初始生成一定数量的身体。

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

public class Snake : MonoBehaviour
{
    public int initBodyCount = 3;  // 初始身体数量

    public GameObject body;  // 身体预制体

    List<GameObject> bodyList = new List<GameObject>();  // 身体列表

    void Start()
    {
        // 初始生成身体
        for (int i = 0; i < initBodyCount; i++)
        {
            GenerateBody();
        }
    }

    /// <summary>
    /// 生成身体
    /// </summary>
    void GenerateBody()
    {
        GameObject obj = Instantiate(body);

        // 已有身体
        if (bodyList.Count > 0)
        {
            // 获取最后一个身体,在它的位置生成
            GameObject lastBody = bodyList[bodyList.Count - 1];
            obj.transform.position = lastBody.transform.position;
        }
        // 没有身体
        else
        {
            // 以蛇头的位置生成身体
            obj.transform.position = transform.position;
        }

        bodyList.Add(obj);
    }
}

拖拽预制体引用,然后覆盖到预制体。

场景里面的 SnakeBody 可以删掉。

引用预制体

运行游戏,会在蛇头的位置生成三个身体,此时身体都是重叠的。

蛇的移动

定义移动方向和移动速度,在 Start 方法中,调用 InvokeRepeating 定时器,根据速度计算出定时器的调用时间间隔,每次调用 Move 方法往当前方向移动。

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

public class Snake : MonoBehaviour
{
    // ...

    public Vector2 direction = Vector2.right;  // 移动方向
    public float speed = 1;  // 移动速度

    void Start()
    {
        // ...

        float time = 1 / speed;
        InvokeRepeating(nameof(Move), time, time);
    }

    /// <summary>
    /// 移动
    /// </summary>
    void Move()
    {
        transform.Translate(direction);
    }
}

此时只有蛇头会移动,身体是不动的。

要让身体跟着蛇头移动,需要在蛇头移动前,标记位置,再把旧的位置赋值给第一个身体。

第一个身体移动前,也要先标记位置,然后赋值给第二个身体。

所以要使用两个 Vector2 变量,循环标记。

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

public class Snake : MonoBehaviour
{
    // ...

    Vector2 posMarkFirst;  // 第一个标记位置
    Vector2 posMarkLast;   // 第二个标记位置

    // ...

    /// <summary>
    /// 移动
    /// </summary>
    void Move()
    {
        // 移动前,先标记旧的位置
        posMarkFirst = transform.position;
        transform.Translate(direction);

        for (int i = 0; i < bodyList.Count; i++)
        {
            // 利用奇偶数,交替标记旧的位置
            if (i % 2 == 0)
            {
                posMarkLast = bodyList[i].transform.position;
                bodyList[i].transform.position = posMarkFirst;
            }
            else
            {
                posMarkFirst = bodyList[i].transform.position;
                bodyList[i].transform.position = posMarkLast;
            }
        }
    }
}

运行游戏,可以看到,蛇开始移动了。

移动

控制方向

Update 方法中,获取键盘输入,改变蛇头的移动方向。

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

public class Snake : MonoBehaviour
{
    // ...

    void Update()
    {
        float h = Input.GetAxisRaw("Horizontal");
        float v = Input.GetAxisRaw("Vertical");

        // 检测到按键输入
        if(h != 0 || v != 0)
        {
            // 改变蛇头的方向
            direction.x = h;
            direction.y = v;
        }
    }
}

运行游戏,可以通过键盘方向键,改变蛇的移动方向。

控制方向

但是问题来了,如上图所示,蛇头可以往反方向移动,还可以斜着移动,这些都是需要排除的情况。

简单分析一下,蛇头的移动方向,用二维向量来变量,有四种情况。

  • 向上 (0, 1)
  • 向下 (0, -1)
  • 向左 (-1, 0)
  • 向右 (1, 0)

当蛇头向右移动时,如果按键输入向左,那么 X 坐标相加等于 0。

同理,向上移动时,如果按键输入向下,那么 Y 坐标相加等于 0。

所以在改变方向时,判断当前方向和按键输入方向的相加情况,进行排除。

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

public class Snake : MonoBehaviour
{
    // ...

    void Update()
    {
        float h = Input.GetAxisRaw("Horizontal");
        float v = Input.GetAxisRaw("Vertical");

        // 检测到按键输入
        if(h != 0 || v != 0)
        {
            if (direction.x + h == 0 || direction.y + v == 0) return;

            // 改变蛇头的方向
            direction.x = h;
            direction.y = v;
        }
    }
}

斜着移动时,也有四种情况。

  • 左上 (-1, 1)
  • 右上 (1, 1)
  • 左下 (-1, -1)
  • 右下 (1, -1)

它们的规律是,要么 X 坐标和 Y 坐标相等,要么 X 坐标和 Y 坐标相加等于 0。

所以在改变方向时,判断以上情况,进行排除。

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

public class Snake : MonoBehaviour
{
    // ...

    void Update()
    {
        float h = Input.GetAxisRaw("Horizontal");
        float v = Input.GetAxisRaw("Vertical");

        // 检测到按键输入
        if(h != 0 || v != 0)
        {
            if (direction.x + h == 0 || direction.y + v == 0) return;
            if (h == v || h + v == 0) return;

            // 改变蛇头的方向
            direction.x = h;
            direction.y = v;
        }
    }
}

但是还没有完全解决问题,当蛇头向右移动时,如果快速按向上键,再按向左键,蛇头依然会往反方向移动,因为以上规则在二次按键时,可以顺利切换方向。

所以,每当定时器触发移动时,还需要再进行方向修正。

我们可以先记录第一次按键的方向,此时该方向是合理的。当触发移动时,如果当前方向和上次移动的方向正好相反,那么判定当前方向是进行了二次按键,把当前方向修正为第一次按键的合理方向。

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

public class Snake : MonoBehaviour
{
    // ...
    
    public Vector2 lastDirection = Vector2.right;   // 上次的移动方向
    public Vector2 firstDirection = Vector2.right;  // 第一次按键的移动方向
    public bool firstPress = true;  // 是否记录第一次按键

    void Update()
    {
        float h = Input.GetAxisRaw("Horizontal");
        float v = Input.GetAxisRaw("Vertical");

        // 检测到按键输入
        if(h != 0 || v != 0)
        {
            if (direction.x + h == 0 || direction.y + v == 0) return;
            if (h == v || h + v == 0) return;

            // 改变蛇头的方向
            direction.x = h;
            direction.y = v;
            
            // 仅记录第一次按键
            if (firstPress)
            {
                firstPress = false;
                firstDirection.x = h;
                firstDirection.y = v;
            }
        }
    }
    
    /// <summary>
    /// 移动
    /// </summary>
    void Move()
    {
        // 当前移动方向和上次方向相反,修正为第一次按键的方向
        if (direction.x + lastDirection.x == 0 || direction.y + lastDirection.y == 0)
        {
            direction.x = firstDirection.x;
            direction.y = firstDirection.y;
        }

        // 移动前,先标记旧的位置
        posMarkFirst = transform.position;
        transform.Translate(direction);

        // 记录上次的移动方向
        lastDirection = direction;
        // 可重新记录第一次按键的移动方向
        firstPress = true;
        
        // ...
    }
}

最终的移动效果。

最终移动效果

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2116985.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

位运算技巧总结

一、常见位运算操作 1、基础位运算 & 按位与 有0则0 | 按位或 有1则1 ^ 按位异或 相同为0 不同为1 2、确定数n的二进制位中第x位是0还是1 目的&#xff1a;是0返回0&#xff0c;是1返回1 (n >> x) & 1 思路&#xff1a;1除了第一位其他位都是0&a…

01初识FreeRTOS【前情回顾篇】

为什么要使用FreeRTOS&#xff1f; 裸机轮询无法避免两个函数相互影响的问题&#xff0c;例如我们使用单片机在进行裸机开发时&#xff0c;我们使用了Delay延时函数&#xff0c;这时我们无法再执行其他的功能代码&#xff0c;需要等延时时间结束再执行其他代码&#xff0c;而使…

通过域名无法访问不到网站,IP可正常访问(DNS污染)

一 DNS被污染 就在刚刚突然访问不到csdn&#xff0c;域名无法访问如下图&#xff1a; 确认DNS是否解析有问题 1 ping 域名 先ping一下域名&#xff0c;ping 域名后得到ip, ping通了如下图&#xff1a; 2 使用IP访问测试 通过ip再访问网站&#xff0c;ip可以正常访问如下图&…

nginx搭配gateway的集群配置

一、nginx在http里配置如下信息 upstream gateway-cluster {server 127.0.0.1:10001;server 127.0.0.1:10002;}server {listen 1000;server_name localhost;location ~/zzw_project/(.*) {proxy_pass http://gateway-cluster/$1;proxy_set_header Host $host; # 代理设…

延迟渲染路径

1. 延迟渲染路径处理光照的方式 延迟渲染路径对光照的数量没有任何限制&#xff0c;并且所有灯光都可以采用逐像素渲染。理论上来说&#xff0c;即 使场景中有成百上千个实时灯光&#xff0c;依然可以保持比较流畅的渲染帧率。它支持法线纹理、阴影等等效果的处理&#xff1b;…

【C++】STL容器详解【下】

目录 一、list容器 1.1 list基本概念 1.2 lsit构造函数 1.3 list数据元素插入和删除操作 1.4 list大小操作 1.5 list赋值操作 1.6 list数据的存取 1.7 list反转排序 二、set/multiset容器 2.1 set/multiset基本概念 2.2 set构造函数 2.3 set赋值操作 2.4 set大小操…

ChatGPT+Simple Mind Map生成思维导图:快速提升学习效率

一、告别杂乱笔记&#xff0c;一键生成清晰思维导图&#xff01; 最近开始学习网络安全&#xff0c;一头扎进了各种协议、漏洞、防御机制的海洋中。信息量巨大&#xff0c;知识点零散&#xff0c;让我很快便陷入了“知识焦虑”——笔记越记越多&#xff0c;却越来越混乱&#…

Django+Vue3前后端分离学习(二)(重写User类)

一、重写User类&#xff1a; 1、首先导入User类&#xff1a; from django.contrib.auth.models import User 2、然后点在User上&#xff0c;按住ctrl 点进去&#xff0c;发现 User类继承AbstractUser Ctrl点进去AbstractUser&#xff0c;然后将此方法全部复制到自己APP的mo…

基于微信小程序+Java+SSM+Vue+MySQL的宿舍管理系统

作者&#xff1a;计算机学姐 开发技术&#xff1a;SpringBoot、SSM、Vue、MySQL、JSP、ElementUI等&#xff0c;“文末源码”。 专栏推荐&#xff1a;前后端分离项目源码、SpringBoot项目源码、SSM项目源码 系统展示 基于微信小程序JavaSSMVueMySQL的宿舍管理系统【附源码文档…

UMI复现基础环境安装配置全流程(三)——UMI环境搭建

一、搭建UMI环境 &#xff08;二&#xff09;中安装了Anaconda3&#xff0c;在此基础上&#xff0c;创建umi环境&#xff0c;在universal_manipulation_interface-main文件夹中打开terminal并输入指令 conda env create -f conda_environment.yaml 此指令根据yaml文件创建环…

​数据编织+敏捷BI,打造企业智赢AI时代的双引擎丨直播预告

大数据产业创新服务媒体 ——聚焦数据 改变商业 随着数字化和智能化浪潮的不断推进&#xff0c;企业如何高效利用数据资源&#xff0c;成为了获取竞争优势的关键。在这样的背景下&#xff0c;我们诚邀您参加即将于2024年9月10日14:30开启的直播——“数据编织敏捷BI&#xff0…

实时通信利器:Web Broadcast Channel API 全面解读

一. 引言 在 Web 开发领域&#xff0c;实时通信一直是一个备受关注的话题。为了更好地实现实时消息传递和跨标签页通信&#xff0c;在 HTML5 规范中引入了 Web Broadcast Channel API。在本文中&#xff0c;我们将解析 Web Broadcast Channel API&#xff0c;探讨其用法以及相…

常见的正则化方法以及L1,L2正则化的简单描述

深度学习中的正则化是通过在模型训练过程中引入某些技术来防止模型过拟合的一种策略。过拟合是指模型在训练数据上表现非常好&#xff0c;但在新的、未见过的数据上表现不佳。正则化通过限制模型的复杂度或对模型参数施加约束&#xff0c;从而提高模型的泛化能力。 常见的正则…

【机器学习】高斯网络的基本概念和应用领域

引言 高斯网络&#xff08;Gaussian Network&#xff09;通常指的是一个概率图模型&#xff0c;其中所有的随机变量&#xff08;或节点&#xff09;都遵循高斯分布 文章目录 引言一、高斯网络&#xff08;Gaussian Network&#xff09;1.1 高斯过程&#xff08;Gaussian Proces…

Notepad++ 修改 About

1. 用这个工具&#xff0c;看标题&#xff0c;修改 1700 里的 Caption, 保存为 xx.exe, 2.修改链接&#xff0c;先准备如上。 2.1 使用插件 Hex Editor&#xff0c;拖入刚保存的 Notepad.exe 到 Notepad.exe, 按 c..S..H 2.2 按 ctrlf 查找 68 00 74 00 74 00 70 00 73 00 3…

ggplot作图基础

目录 ggplot作图语法 散点图 折线图 group分组 face_wrap()图像切片摆放 facet_grid()交叉分组切片 条形图 2.1 单组变量条形图 2.2 多维展示变量 直方图有与密度估计 直方图 密度估计图 ..density..语法和stat“density” ggplot作图语法 ggplot作图是将数据按需要进…

锡林郭勒奶酪品牌呼和浩特市大召店盛大开业

礼献中秋&#xff0c;香飘乳都。为进一步拓展锡林郭勒奶酪区域公用品牌产品销售渠道&#xff0c;9月8日&#xff0c;锡林郭勒奶酪区域公用品牌大召店在呼和浩特市大召广场月明楼隆重开业&#xff0c;现场为第三批新授权的39家奶酪生产经营主体代表授牌。至此&#xff0c;锡林郭…

Debian 12如何关闭防火墙

在Debian 12中&#xff0c;默认的防火墙管理工具是ufw&#xff08;Uncomplicated Firewall&#xff09;。您可以使用以下命令来关闭防火墙&#xff1a; 关闭防火墙&#xff1a; sudo ufw disable查看防火墙状态&#xff1a; sudo ufw status如果需要重新开启防火墙&#xff1a;…

9.8javaweb项目总结

1.主界面用户信息显示 登录成功后&#xff0c;将用户信息存储在记录在 localStorage中&#xff0c;然后进入界面之前通过js来渲染主界面 存储用户信息 将用户信息渲染在主界面上&#xff0c;并且头像设置跳转&#xff0c;到个人资料界面 这里数据库中还没有设置相关信息 2.模糊…

数学建模笔记—— 主成分分析(PCA)

数学建模笔记—— 主成分分析 主成分分析1. 基本原理1.1 主成分分析方法1.2 数据降维1.3 主成分分析原理1.4 主成分分析思想 2. PCA的计算步骤3. 典型例题4. 主成分分析说明5. python代码实现 主成分分析 1. 基本原理 在实际问题研究中,多变量问题是经常会遇到的。变量太多,无…