第十三章 移动和旋转(上)
移动和旋转是游戏对象最频繁地操作。我们上个章节简单介绍了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)、如何模拟双晶体的双折射偏振器以及如何计算偏振器的消光比。(联系我们获取文章附件) 什么是双折射现象 一般的光学材料都是均匀的各向同性的,也就是说无论光…...
SpringBoot-17-MyBatis动态SQL标签之常用标签
文章目录 1 代码1.1 实体User.java1.2 接口UserMapper.java1.3 映射UserMapper.xml1.3.1 标签if1.3.2 标签if和where1.3.3 标签choose和when和otherwise1.4 UserController.java2 常用动态SQL标签2.1 标签set2.1.1 UserMapper.java2.1.2 UserMapper.xml2.1.3 UserController.ja…...
深入浅出Asp.Net Core MVC应用开发系列-AspNetCore中的日志记录
ASP.NET Core 是一个跨平台的开源框架,用于在 Windows、macOS 或 Linux 上生成基于云的新式 Web 应用。 ASP.NET Core 中的日志记录 .NET 通过 ILogger API 支持高性能结构化日志记录,以帮助监视应用程序行为和诊断问题。 可以通过配置不同的记录提供程…...
synchronized 学习
学习源: https://www.bilibili.com/video/BV1aJ411V763?spm_id_from333.788.videopod.episodes&vd_source32e1c41a9370911ab06d12fbc36c4ebc 1.应用场景 不超卖,也要考虑性能问题(场景) 2.常见面试问题: sync出…...
Linux链表操作全解析
Linux C语言链表深度解析与实战技巧 一、链表基础概念与内核链表优势1.1 为什么使用链表?1.2 Linux 内核链表与用户态链表的区别 二、内核链表结构与宏解析常用宏/函数 三、内核链表的优点四、用户态链表示例五、双向循环链表在内核中的实现优势5.1 插入效率5.2 安全…...
JavaScript 中的 ES|QL:利用 Apache Arrow 工具
作者:来自 Elastic Jeffrey Rengifo 学习如何将 ES|QL 与 JavaScript 的 Apache Arrow 客户端工具一起使用。 想获得 Elastic 认证吗?了解下一期 Elasticsearch Engineer 培训的时间吧! Elasticsearch 拥有众多新功能,助你为自己…...
Qt Widget类解析与代码注释
#include "widget.h" #include "ui_widget.h"Widget::Widget(QWidget *parent): QWidget(parent), ui(new Ui::Widget) {ui->setupUi(this); }Widget::~Widget() {delete ui; }//解释这串代码,写上注释 当然可以!这段代码是 Qt …...
【项目实战】通过多模态+LangGraph实现PPT生成助手
PPT自动生成系统 基于LangGraph的PPT自动生成系统,可以将Markdown文档自动转换为PPT演示文稿。 功能特点 Markdown解析:自动解析Markdown文档结构PPT模板分析:分析PPT模板的布局和风格智能布局决策:匹配内容与合适的PPT布局自动…...
跨链模式:多链互操作架构与性能扩展方案
跨链模式:多链互操作架构与性能扩展方案 ——构建下一代区块链互联网的技术基石 一、跨链架构的核心范式演进 1. 分层协议栈:模块化解耦设计 现代跨链系统采用分层协议栈实现灵活扩展(H2Cross架构): 适配层…...
【论文阅读28】-CNN-BiLSTM-Attention-(2024)
本文把滑坡位移序列拆开、筛优质因子,再用 CNN-BiLSTM-Attention 来动态预测每个子序列,最后重构出总位移,预测效果超越传统模型。 文章目录 1 引言2 方法2.1 位移时间序列加性模型2.2 变分模态分解 (VMD) 具体步骤2.3.1 样本熵(S…...
mysql已经安装,但是通过rpm -q 没有找mysql相关的已安装包
文章目录 现象:mysql已经安装,但是通过rpm -q 没有找mysql相关的已安装包遇到 rpm 命令找不到已经安装的 MySQL 包时,可能是因为以下几个原因:1.MySQL 不是通过 RPM 包安装的2.RPM 数据库损坏3.使用了不同的包名或路径4.使用其他包…...
