3D是非常酷的技术,同时也就意味着更多的工作,上次的简单介绍之后,这次还要讲更多2D到3D的新概念。
基于时间的三维移动
我们使用Vector3类来进行3D上的移动,与2D非常类似,看下面一个例子:
直升机A在(-6, 2, 2)的位置上,目标是直升机B(7, 5, 10),A想摧毁B,所以发射了一枚火箭AB,现在我们得把火箭的运动轨迹过程给画出来,否则一点发射敌机就炸了,多没意思啊~~ 通过计算出两者之间的向量为(13, 3, 8),然后单位化这个向量,这样就可以在运动中用到了,下面的代码做了这些事情。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
from gameobjects.vector3 import * A = (–6, 2, 2) B = (7, 5, 10) plasma_speed = 100. # meters per second AB = Vector3.from_points(A, B) print "Vector to droid is", AB distance_to_target = AB.get_magnitude() print "Distance to droid is", distance_to_target, "meters" plasma_heading = AB.get_normalized() print "Heading is", plasma_heading #######输出结果######### Vector to droid is (13, 3, 8) Distance to droid is 15.5563491861 meters Heading is (0.835672, 0.192847, 0.514259) |
然后不停的重绘火箭的位置,用这个语句:
rocket_location += heading * time_passed_seconds * speed
不过我们还不能直接在pygame中绘制3D物体,得先学习一下下面讲的,“如何把3D转换为2D”。
3D透视
如果您初中美术认真学了的话,应该会知道这里要讲什么,还记得当初我们是如何在纸上画立方体的?
忘了?OK,从头开始说起吧,存储、计算3D坐标是非常容易的,但是要把它展现到屏幕上就不那么简单了,因为pygame中所有的绘图函数都只接受2D坐标,因此,我们必须把这些3D的坐标投影到2D的图面上。
平行投影
最简单的投影方法是——把第三个坐标z坐标给丢弃,用这样的一个简单的函数就可以做到:
1 2 |
def parallel_project(vector3): return (vector3.x, vector3.y) |
尽管这样的转换简单又快速,我们却不能用它。为什么?效果太糟糕了,我们甚至无法在最终的画面上感受到一点立体的影子,这个世界看起来还是平的,没有那个物体看起来比其他物体更远或更近。就好像我下边这幅图一样。
立体投影
在3D游戏中使用的更为广泛且合理的技术是立体投影,因为它的结果更为真实。立体投影把远处的物体缩小了,也就是使用透视法(foreshortening),如下图所示,然后下面是我们的转换函数,看起来也很简单:
1 2 3 |
def perspective_project(vector3, d): x, y, z = vector3 return (x * d/z, –y * d/z) |
与上一个转换函数不同的是,这个转换函数还接受一个d参数(后面讨论),然后所有的x、y坐标都会接受这个d的洗礼,同时z也会插一脚,把原本的坐标进行缩放。
d的意思是视距(viewing distance),也就是摄像头到3D世界物体在屏幕上的像素体现之间的距离。比如说,一个在(10, 5, 100)的物体移动到了(11, 5, 100),视距是100的时候,它在屏幕上就刚好移动了1个像素,但如果它的z不是100,或者视距不是100,那么可能移动距离就不再是1个像素的距离。有些抽象,不过玩过3D游戏的话(这里指国外的3D大作),都有一种滚轮调节远近的功能,这就是视距(当然调的时候视野也会变化,这个下面说)。
在我们玩游戏的时候,视距就为我们的眼睛到屏幕的直线距离(以像素为单位)。
视野
那么我们怎么选取一个好的d呢?我们当然可以不断调整实验来得到一个,不过我们还可以通过视野(field of view)来计算一个出来。视野也就是在一个时刻能看到的角度。看一下左图的视野和视距的关系,可以看到两者是有制约关系,当视野角度(fov)增大的时候,d就会减小;而d增加的话,视野角度就会减小,能看到的东西也就变少了。
视野是决定在3D画面上展现多少东西的绝好武器,然后我们还需要一个d来决定透视深度,使用一点点三角只是,我们就可以从fov计算出d,写一下下面的代码学习学习:
在Internet上,你总是能找到99%以上的需要的别人写好的代码。不过偶尔还是要自己写一下的,不用担心自己的数学是不及格的,这个很简单~ 很多时候实际动手试一下,你就能明白更多。
1 2 3 4 |
from math import tan def calculate_viewing_distance(fov, screen_width): d = (screen_width/2.0) / tan(fov/2.0) return d |
fov角度可能取45~60°比较合适,这样看起来很自然。当然每个人每个游戏都有特别的地方,比如FPS的话,fov可能比较大;而策略性游戏的fov角度会比较小,这样可以看到更多的东西。很多时候还需要不停的变化fov,最明显的CS中的狙击枪(从没玩过,不过听过),开出来和关掉是完全不同的效果,改的就是视野角度。
今天又是补充了一大堆知识,等不及了吧~我们下一章就能看到一个用pygame画就的3D世界了!