博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
码农干货系列【3】--割绳子(cut the rope)制作点滴:旋转(rotation)
阅读量:5060 次
发布时间:2019-06-12

本文共 3340 字,大约阅读时间需要 11 分钟。

旋转

在大量的游戏开发过程当中,旋转是经常被开发者使用的,通常需要得到旋转后目标点的坐标。旋转分很多种类:2D游戏世界中,以某一点为旋转目标;3D游戏世界中,以轴为旋转目标。所以本文将旋转分为四类,涵盖所有旋转的情况:

绕点旋转(2D)

绕坐标轴(x/y/z)旋转(3D)

绕坐标轴的平行轴旋转(3D)

绕任意轴旋转(3D)

绕点旋转

在绕点旋转的时候,需要传入两个参数,一个是目标中心点p(即绕着哪个点旋转),另一个参数是旋转的角度theta。所以为Vector2扩展如下方法:

rotateSelf: function (p, theta) {
var v = this.sub(p); theta *= Math.PI / 180; var R = [[Math.cos(theta), -Math.sin(theta)], [Math.sin(theta), Math.cos(theta)]]; this.x = p.x + R[0][0] * v.x + R[0][1] * v.y; this.y = p.y + R[1][0] * v.x + R[1][1] * v.y; },
以上代码的具体的流程如下:
a.var v=this.sub(p)===>先过原点(把p点当作原点),v是相对于原点p点的坐标
b.var R=[……..]===>根据角度生成旋转矩阵
c.求出v点绕着原点(p点)旋转后的向量坐标(R[0][0] * v.x + R[0][1] * v.y,R[1][0] * v.x + R[1][1] * v.y)
d.把v点的向量坐标累加回p点,即得出最后旋转后点的坐标

要理解好过原点,要追溯到线性函数。最简单的例子就是我们把f(x)=kx+b的b割舍掉,成为f(x)=kx的形式。只有过原点的直线才能被成为一元线性函数。因为不过原点的直线不满足我们对线性函数比例性的要求。而矩阵是向量的数组,向量的表达方式是基于原点的。

所以:矩阵变换的核心和基础就是理解好过原点,所以才会有上面来回移动的这个过程。

绕坐标轴(x/y/z)旋转

在3D世界中,绕坐标轴旋转的的本质就是3D中的2D切面中的旋转。通常我们定义一个矩阵类来辅助向量类的计算:

Matrix.RotationX = function(t) {
var c = Math.cos(t), s = Math.sin(t); return Matrix.create([ [ 1, 0, 0 ], [ 0, c, -s ], [ 0, s, c ] ]); }; Matrix.RotationY = function(t) {
var c = Math.cos(t), s = Math.sin(t); return Matrix.create([ [ c, 0, s ], [ 0, 1, 0 ], [ -s, 0, c ] ]); }; Matrix.RotationZ = function(t) {
var c = Math.cos(t), s = Math.sin(t); return Matrix.create([ [ c, -s, 0 ], [ s, c, 0 ], [ 0, 0, 1 ] ]); };
可以看到:
绕着X轴旋转矩阵变换,x坐标不变
绕着Y轴旋转矩阵变换,y坐标不变
绕着Z轴旋转矩阵变换,z坐标不变

绕坐标轴的平行轴旋转

绕坐标轴的平行轴的思路和绕点旋转的思路一致,我们为Vector3扩展如下方法:

rotateXSelf: function (p, theta) {
var v = this.sub(p); theta *= Math.PI / 180; var R = [[Math.cos(theta), -Math.sin(theta)], [Math.sin(theta), Math.cos(theta)]]; this.y = p.y + R[0][0] * v.y + R[0][1] * v.z; this.z = p.z + R[1][0] * v.y + R[1][1] * v.z; }, rotateYSelf: function (p, theta) {
var v = this.sub(p); theta *= Math.PI / 180; var R = [[Math.cos(theta), -Math.sin(theta)], [Math.sin(theta), Math.cos(theta)]]; this.x = p.x + R[0][0] * v.x + R[0][1] * v.z; this.z = p.z + R[1][0] * v.x + R[1][1] * v.z; }, rotateZSelf: function (p, theta) {
var v = this.sub(p); theta *= Math.PI / 180; var R = [[Math.cos(theta), -Math.sin(theta)], [Math.sin(theta), Math.cos(theta)]]; this.x = p.x + R[0][0] * v.x + R[0][1] * v.y; this.y = p.y + R[1][0] * v.x + R[1][1] * v.y; }

这里的p点满足的条件是:要旋转的点与p点的连线垂直于旋转轴,旋转轴过p点。

以上代码的具体的流程如下:
a.var v=this.sub(p)===>先过原点(把p点当作原点),v是相对于原点p点的坐标
b.var R=[……..]===>根据角度生成旋转矩阵
c.求出v点绕着原点(p点)旋转后的向量坐标
d.把v点的向量坐标累加回p点,即得出最后旋转后点的坐标

和2D绕点旋转一样。

绕任意轴旋转

Matrix.Rotation = function(theta, a) {
var axis = a.dup(); if (axis.elements.length != 3) { return null; } var mod = axis.modulus(); var x = axis.elements[0]/mod, y = axis.elements[1]/mod, z = axis.elements[2]/mod; var s = Math.sin(theta), c = Math.cos(theta), t = 1 - c; return Matrix.create([ [ t*x*x + c, t*x*y - s*z, t*x*z + s*y ], [ t*x*y + s*z, t*y*y + c, t*y*z - s*x ], [ t*x*z - s*y, t*y*z + s*x, t*z*z + c ] ]); };

详细推导过程传送门:

举一个栗子

这是我制作《割绳子》的第一关中的部分效果,比IE官网的难度稍大一点,三颗星星不是闭着眼睛割就能够得到,也要找准时机果断割绳子。

请使用现代浏览器观看在线演示!

小结

本文主要是通过一点点线性代数的知识,解决旋转相关的问题。线性代数的应用非常广泛,在光线追踪、物理引擎、图像识别、实时碰撞检测等重要领域都有着不可替代的作用和地位。当然,除了计算机行业,在其他行业,比如电子工程、3D影片制作渲染、土木工程等,都有这重要的作用和地位。

更多干货,敬请期待~~~

转载于:https://www.cnblogs.com/iamzhanglei/archive/2012/06/11/2544304.html

你可能感兴趣的文章
非对称加密
查看>>
bzoj 3413: 匹配
查看>>
从下周开始就要采用网上记录值班日志了
查看>>
在qq中可以使用添加标签功能
查看>>
eclipse 自定义布局
查看>>
团队项目开发客户端——登录子系统的设计
查看>>
【AppScan心得】IBM Rational AppScan 无法记录登录序列
查看>>
[翻译] USING GIT IN XCODE [4] 在XCODE中使用GIT[4]
查看>>
简化通知中心的使用
查看>>
SpringMVC的@Validated校验注解使用方法
查看>>
Python之os模块
查看>>
IO—》Properties类&序列化流与反序列化流
查看>>
【蓝桥杯】PREV-21 回文数字
查看>>
html 简介
查看>>
python使用上下文对代码片段进行计时,非装饰器
查看>>
js中比较实用的函数用法
查看>>
安装预览版镜像后无法检测到预览版更新的解决方案
查看>>
【bzoj5099】[POI2018]Pionek 双指针法
查看>>
别让安全问题拖慢了 DevOps!
查看>>
JAR打包和运行
查看>>