第十三章 移动和旋转(上)
移动和旋转是游戏对象最频繁地操作。我们上个章节简单介绍了Cube的移动和旋转。移动是修改transform的position属性,旋转是修改transform的eulerAngles(欧拉角)属性,两者属性值均可以使用Vector3向量来实现。需要大家注意的是,transform.forward和Vector3.forward的区别(参考坐标系是不一样的)。接下来,我们使用transform的Translate方法来进行移动。首先,我在重新创建一个“SampleScene4”场景。我们在该创建中同样创建一个“Cube”游戏对象和“CubeTranslate.cs”脚本文件,并附加两者在一起。


以下是脚本代码内容
using System.Collections;
using System.Collections.Generic;
using UnityEngine;public class CubeTranslate : MonoBehaviour
{// Update is called once per framevoid Update(){if (Input.GetKeyDown(KeyCode.W)){transform.Translate(new Vector3(0, 0, 1));}// 向左旋转45度if (Input.GetKeyDown(KeyCode.Q)){transform.eulerAngles += new Vector3(0, -45, 0);}// 向右旋转45度if (Input.GetKeyDown(KeyCode.E)){transform.eulerAngles += new Vector3(0, 45, 0);}}
}
我们实现的功能还是之前的一样,按下“W”键向前移动。为了方便观察Cube的移动,我们需要将相机放置Cube的正上方“俯视”Cube。摄像机修改参数如下:

为什么要修改成这些数值,我们上一个章节介绍过了,修改后效果如下:

然后,我们Play工程,查看运行结果。
测试结果就是相对于本地坐标系的移动,也就是游戏对象的正前方移动。



很明显,默认情况下,Translate(Vector3(0, 0, 1))方法与transform.forward是等效的,也就是相对于本地坐标系而言。那么,如何改成世界坐标系的移动呢,不受游戏物体旋转影响呢?
其实该方法还有第二个参数,也就是Space.Self本地坐标系和Space.World世界坐标系。
显然,如果我们不填写第二个参数的话,默认就是Space.Self本地坐标系,我们改一下
if (Input.GetKeyDown(KeyCode.W)){//transform.Translate(new Vector3(0, 0, 1));transform.Translate(new Vector3(0, 0, 1), Space.World);}
接下来,我们在重新测试,发现现在的移动就是相对于世界坐标系的啦,截图就不添加了。总结:游戏对象的移动可以通过在transform.translate()方法实现,也可以直接修改transform.position属性。这里需要区分的是世界坐标系还是自身坐标系。如下:
世界坐标系z轴前进:transform.position += vector3.forward
自身坐标系z轴前进:transform.position += transform.forward
世界坐标系z轴前进:transform.translate(vector3.forward, space.world)
自身坐标系z轴前进:transform.translate(vector3.forward, space.self)
如果是space.world的话,还可以使用transform.forward;但是最好不要将transform.forward应用到space.self上。因此还是建议translate方法+ vector3向量搭配使用进行移动。
接下来,我们来说一说旋转,这个是比较复杂的。我们之前的旋转是通过修改transform的eulerAngles(欧拉角)实现的。但是,在我们的印象中,游戏对象的旋转应该是rotation属性,尤其是在Inspector检视面板中,我们也能够看到这个名字的属性。例如下面的截图就是我们对摄像机对象做的旋转设置,也就是在X轴方向上面旋转90度。请注意,此时的参考坐标系是父对象,如果没有父对象就是世界坐标系。

因此,显而易见这是欧拉角数值。但是在Unity API中,这个rotation属性是一个四元数(Quaternion)。关于四元数的概念以及它相对于欧拉角的优势,我们这里不在介绍,它唯一的缺点就是不如欧拉角简单直观。因此,我们往往借助欧拉角转换成四元数再控制游戏对象旋转。接下来,我们修改一下代码,如下所示:
// 向左旋转45度if (Input.GetKeyDown(KeyCode.Q)){//transform.eulerAngles += new Vector3(0, -45, 0);transform.rotation = Quaternion.Euler(new Vector3(0, -45, 0));}// 向右旋转45度if (Input.GetKeyDown(KeyCode.E)){//transform.eulerAngles += new Vector3(0, 45, 0);transform.rotation = Quaternion.Euler(new Vector3(0, 45, 0));}
上面的代码实现了“Q”和“E”键分别向左右两方向按照45度单位的旋转。但是当我们运行当前工程的时候,发现它并不能连续旋转,而只是旋转一下就停止了。我们很容易想到使用“+=”操作符,但是很遗憾的是,transform.rotation并不支持这样的运算。因此,我们只能换一种方式来解决这个问题,我们可以使用一个类全局变量来存储游戏对象的旋转数据。
using System.Collections;
using System.Collections.Generic;
using UnityEngine;public class CubeTranslate : MonoBehaviour
{// Y轴旋转角度值private float y = 0.0f;// Update is called once per framevoid Update(){if (Input.GetKeyDown(KeyCode.W)){//transform.Translate(new Vector3(0, 0, 1));transform.Translate(new Vector3(0, 0, 1), Space.World);}// 向左旋转45度if (Input.GetKeyDown(KeyCode.Q)){//transform.eulerAngles += new Vector3(0, -45, 0);//transform.rotation = Quaternion.Euler(new Vector3(0, -45, 0));y -= 45;transform.rotation = Quaternion.Euler(new Vector3(0, y, 0));}// 向右旋转45度if (Input.GetKeyDown(KeyCode.E)){//transform.eulerAngles += new Vector3(0, 45, 0);//transform.rotation = Quaternion.Euler(new Vector3(0, 45, 0));y += 45;transform.rotation = Quaternion.Euler(new Vector3(0, y, 0));}}
}
重新运行后,我们发现Cube可以进行“连续性”的旋转了,如下Gif图所示:

我们发现使用transform.rotation进行旋转的话,还是比较费劲的。但是,有一点大家要明白,这里的旋转和上面的移动是相同的道理,同样存在世界坐标系和局部坐标系的前提条件。这个当然取决于我们使用Vector3类还是使用Transform类。接下来我们就使用transform提供的Rotate方法来进行旋转,它的参数是欧拉角,比较容易让人直观的理解,而且也提供了坐标系参数的指定(Space.Self或者Space.World,当然默认还是Space.Self)。
// 向左旋转45度if (Input.GetKeyDown(KeyCode.Q)){//transform.eulerAngles += new Vector3(0, -45, 0);//transform.rotation = Quaternion.Euler(new Vector3(0, -45, 0));//y -= 45;//transform.rotation = Quaternion.Euler(new Vector3(0, y, 0));transform.Rotate(new Vector3(0, -45, 0));}// 向右旋转45度if (Input.GetKeyDown(KeyCode.E)){//transform.eulerAngles += new Vector3(0, 45, 0);//transform.rotation = Quaternion.Euler(new Vector3(0, 45, 0));//y += 45;//transform.rotation = Quaternion.Euler(new Vector3(0, y, 0));transform.Rotate(new Vector3(0, 45, 0));}
我们发现transform.Rotate使用起来就比较简单了。当我们Play当前工程之后,我们就发现效果和之前是一样的(可以连续的旋转,而不是只旋转一下)。但是,请大家明白的是,之前的旋转是相对于世界坐标系的Y轴,现在的旋转是相对于游戏对象本地坐标系的Y轴,之所以效果一样是,是因为两者坐标系重合了而已。

从Scene视图中我们可以看到,Cube对象坐标系和世界坐标系是一致的。接下来,我们调整Cube,让两个坐标系不一致,我们让Cube沿着Z轴顺时针旋转90度,也就是修改Cube的Inspector视图中的Rotation中的Z值为“-90”,效果如下所示:


为什么“-90”度是顺时针旋转呢?这个取决于我们的观察方向。在默认的左手坐标系下,负值就是顺时针旋转;当然右手坐标系下,正值是顺时针旋转。此时世界坐标系的Y轴仍然是向上的方向,而游戏对象Cube的Y轴是向右的。那么,此时如果我们让Cube在Y轴上按照两个不同坐标系进行旋转的话,那肯定是不一样了。为了能够看到这个效果,我们修改摄像机的位置参数,从侧面观察cube的旋转。

然后(保持相机选中状态)我们点击菜单栏“GameObject”->“Align View to Selected”

此时,我们的Scene视角与Game视角一致。如果是本地坐标系的话,Y轴是向里的。因此围绕它旋转的话,Cube应该是类似“车轮滚动”的方式进行旋转。
// 向左旋转45度
transform.Rotate(new Vector3(0, -45, 0));// 向右旋转45度
transform.Rotate(new Vector3(0, 45, 0));
然后我们Play工程,查看运行结果,Gif图如下

如果是世界坐标系的话,Y轴是向上的。如果它应该是“陀螺”旋转的方式。
//transform.Rotate(new Vector3(0, -45, 0));
transform.Rotate(new Vector3(0, -45, 0), Space.World);//transform.Rotate(new Vector3(0, 45, 0));
transform.Rotate(new Vector3(0, 45, 0),Space.World);
我们直接Play工程,查看Gif效果图吧

相关文章:
第十三章 移动和旋转(上)
移动和旋转是游戏对象最频繁地操作。我们上个章节简单介绍了Cube的移动和旋转。移动是修改transform的position属性,旋转是修改transform的eulerAngles(欧拉角)属性,两者属性值均可以使用Vector3向量来实现。需要大家注意的是&…...
视频文件切片
1.为什么网络点播系统使用m3u8更有优势?为何点播要用M3U8来搞?存成一个文件不更好吗? 一个MP4文件可能几百M或几个G,如果读取整个MP4文件的信息并且需要下载一段内容,首次打开播放超慢(加载时间长)。如果把…...
维生素的缺乏与生理功能,是否需要补充维生素【持续学习】
health & nutrition 学习自河南大学丁勇老师:https://space.bilibili.com/510028707 去医院查体内维生素缺啥:营养科或内科开单子 直接门诊查个维生素就可以。9项不到600块 正常吃饭,保湿和防晒 伤口愈合慢——蛋白质,vc 干燥…...
CUDA下载,以及下载GPU版本的pytorch
一、下载anaconda 因为这步我之前就下好了,主要参考这个链接:史上最全最详细的Anaconda安装教程 二、下载CUDA 1.首先观察自己需要什么版本的CUDA,以及是否安装过CUDA 先cmd,输入命令 nvidia-smi结果如下,所以我们…...
学习笔记:c存储类
✨博文作者:烟雨孤舟 💖 喜欢的可以 点赞 收藏 关注哦~~ ✍️ 作者简介: 一个热爱大数据的学习者 文章目录 目录 文章目录 简介 auto 存储类 register 存储类 static 存储类 extern 存储类 总结 简介 存储类定义 C 程序中变量/函数的的存储位置…...
236. 二叉树的最近公共祖先【190】
难度等级:中等 上一篇算法: 103. 二叉树的锯齿形层序遍历【191】 力扣此题地址: 236. 二叉树的最近公共祖先 - 力扣(Leetcode) 1.题目:236. 二叉树的最近公共祖先 给定一个二叉树, 找到该树中两个指定节点…...
即时配送,即时很重要!商家能不能盈利,“快”是源头
“家里水果没有了,选几样叫个跑腿送来吧。” “现在得囤点布洛芬了,我从网上下单。” “同城配送真是太及时、太方便了。” 最近一段时间,如果要问有什么产业突然兴起的话,即时零售无疑是市场最受欢迎的产业。甚至有种说法&…...
ChatGPT原理剖析
文章目录 ChatGPT常见误解1. 罐头回应2. 网络搜寻重组 ChatGPT真正做的事——文字接龙ChatGPT背后的关键技术——预训练(Pre-train)一般机器是怎样学习的? ChatGPT带来的研究问题1. 如何精准提出需求2. 如何更改错误3. 侦测AI生成的物件4. 不…...
「C/C++」C/C++软件跨平台思维
博客主页:何曾参静谧的博客 文章专栏:「C/C」C/C学习 目录 相关术语一、编写可移植的代码:二、使用跨平台的C库和框架:三、进行兼容性测试:四、用户界面设计: 相关术语 跨平台思维:是指在软件开…...
c# 通过界面上填写的信息输出到对应的word中,并另存为一个新的文件
c# 通过界面上填写的信息输出到对应的word中,并另存为一个新的文件 using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Drawing; using System.Linq; using System.Text; using System.Threading.Tas…...
HTML+CSS+JS 学习笔记(四)———jQuery
🌱博客主页:大寄一场. 🌱系列专栏:前端 🌱往期回顾: 😘博客制作不易欢迎各位👍点赞⭐收藏➕关注 目录 jQuery 基础 jQuery 概述 下载与配置jQuery 2. 配置jQuery jQuery 选…...
TryHackMe-Mnemonic(boot2root)
Mnemonic I hope you have fun. 端口扫描 循例nmap FTP枚举 尝试anonymous Web枚举 进80 gobuster扫 对着webmasters再扫一下 对着backups继续扫 下载zip文件,发现有密码 zip2john john直接爆 查看note.txt, 给出了ftpuser hydra直接爆ftp 进到ftp 用wget下载所…...
Nacos注册中心的使用
文章目录 Nacos注册中心1. 服务注册到nacos1)引入依赖2)配置nacos地址3)重启 2.服务分级存储模型2.1.给user-service配置集群2.2.同集群优先的负载均衡 3.权重配置 Nacos注册中心 国内公司一般都推崇阿里巴巴的技术,比如注册中心…...
项目中别用 “! = null“ 做判空了
问题 为了避免空指针调用,我们经常会看到这样的语句: if (someobject ! null) {someobject.doCalc();}最终,项目中会存在大量判空代码,丑陋繁杂。。。如何避免这种情况?是否滥用了判空? 最终,项目中会存在…...
MySQL数据库——MySQL子查询
子查询是 MySQL 中比较常用的查询方法,通过子查询可以实现多表查询。子查询指将一个查询语句嵌套在另一个查询语句中。子查询可以在 SELECT、UPDATE 和 DELETE 语句中使用,而且可以进行多层嵌套。在实际开发时,子查询经常出现在 WHERE 子句中…...
工具链和其他-超级好用的web调试工具whistle
目录 whistle介绍 整体结构 能力 规则 6个使用场景示例 1.修改Host 2.代理 3.替换文件(线上报错时) 4.替换UA 5.远程调试 6.JS注入 互动 whistle介绍 整体结构 安装: npm install whistle -g cli:whistle help 启动…...
ROS第四十三节——定位
https://download.csdn.net/download/qq_45685327/87725276 1.新建launch文件 关于launch文件的实现,在amcl功能包下的example目录已经给出了示例,可以作为参考,具体实现: roscd amcl ls examples gedit amcl_diff.launch 该目录下会列出两…...
2023年第二十届五一数学建模竞赛题目 C题详细思路
详细思路以及发布视频版,大家可以去观看,这里是对应的文字版,内容相差不多。 C题:“双碳”目标下低碳建筑研究 C题的问题设置其实是本次比赛最简单的一道,就是简单的综合评价预测模型。真正提升C题难度的其实是C题的…...
模块化编程原理示意图--CommonJS 模块编程--ES6 模块编程思路分析/图解--三种导出形式--全部代码示例
目录 模块化编程 基本介绍 模块化编程原理示意图 模块化编程分类 CommonJS 模块编程 介绍 应用实例 1. 需求说明 2. 思路分析/图解 3. 代码实现 function.js use.html use.js ES6 模块编程 介绍 需求说明 思路分析/图解 代码实现 common.js use_common.js …...
Ansys Zemax | 如何模拟双折射偏振器件
这篇文章介绍了什么是双折射现象、如何在OpticStudio中模拟双折射 (birefringence)、如何模拟双晶体的双折射偏振器以及如何计算偏振器的消光比。(联系我们获取文章附件) 什么是双折射现象 一般的光学材料都是均匀的各向同性的,也就是说无论光…...
手游刚开服就被攻击怎么办?如何防御DDoS?
开服初期是手游最脆弱的阶段,极易成为DDoS攻击的目标。一旦遭遇攻击,可能导致服务器瘫痪、玩家流失,甚至造成巨大经济损失。本文为开发者提供一套简洁有效的应急与防御方案,帮助快速应对并构建长期防护体系。 一、遭遇攻击的紧急应…...
Zustand 状态管理库:极简而强大的解决方案
Zustand 是一个轻量级、快速和可扩展的状态管理库,特别适合 React 应用。它以简洁的 API 和高效的性能解决了 Redux 等状态管理方案中的繁琐问题。 核心优势对比 基本使用指南 1. 创建 Store // store.js import create from zustandconst useStore create((set)…...
MongoDB学习和应用(高效的非关系型数据库)
一丶 MongoDB简介 对于社交类软件的功能,我们需要对它的功能特点进行分析: 数据量会随着用户数增大而增大读多写少价值较低非好友看不到其动态信息地理位置的查询… 针对以上特点进行分析各大存储工具: mysql:关系型数据库&am…...
【Linux】C语言执行shell指令
在C语言中执行Shell指令 在C语言中,有几种方法可以执行Shell指令: 1. 使用system()函数 这是最简单的方法,包含在stdlib.h头文件中: #include <stdlib.h>int main() {system("ls -l"); // 执行ls -l命令retu…...
镜像里切换为普通用户
如果你登录远程虚拟机默认就是 root 用户,但你不希望用 root 权限运行 ns-3(这是对的,ns3 工具会拒绝 root),你可以按以下方法创建一个 非 root 用户账号 并切换到它运行 ns-3。 一次性解决方案:创建非 roo…...
Linux云原生安全:零信任架构与机密计算
Linux云原生安全:零信任架构与机密计算 构建坚不可摧的云原生防御体系 引言:云原生安全的范式革命 随着云原生技术的普及,安全边界正在从传统的网络边界向工作负载内部转移。Gartner预测,到2025年,零信任架构将成为超…...
Swagger和OpenApi的前世今生
Swagger与OpenAPI的关系演进是API标准化进程中的重要篇章,二者共同塑造了现代RESTful API的开发范式。 本期就扒一扒其技术演进的关键节点与核心逻辑: 🔄 一、起源与初创期:Swagger的诞生(2010-2014) 核心…...
Unity | AmplifyShaderEditor插件基础(第七集:平面波动shader)
目录 一、👋🏻前言 二、😈sinx波动的基本原理 三、😈波动起来 1.sinx节点介绍 2.vertexPosition 3.集成Vector3 a.节点Append b.连起来 4.波动起来 a.波动的原理 b.时间节点 c.sinx的处理 四、🌊波动优化…...
Typeerror: cannot read properties of undefined (reading ‘XXX‘)
最近需要在离线机器上运行软件,所以得把软件用docker打包起来,大部分功能都没问题,出了一个奇怪的事情。同样的代码,在本机上用vscode可以运行起来,但是打包之后在docker里出现了问题。使用的是dialog组件,…...
算法:模拟
1.替换所有的问号 1576. 替换所有的问号 - 力扣(LeetCode) 遍历字符串:通过外层循环逐一检查每个字符。遇到 ? 时处理: 内层循环遍历小写字母(a 到 z)。对每个字母检查是否满足: 与…...
