用Python和Pygame写游戏-从入门到精通(17)

By | 2011/08/06

最近有些忙,没有更新这个系列,不行啊不行,抓紧更新一篇,这几次可是3D啊3D,多么诱人的词啊……

游戏通常希望营造一个真实的世界,越接近真实越好啊,这样的代入感会很强。在早期,由于硬件的限制,游戏只能提供一些2D的图像,因为这对于电脑绘图是最容易的。还好随着技术发展,现在的显卡已经可以画出很逼真的3D画面了,所以“硬件杀手”游戏层出不穷,贫困游戏迷的噩梦啊。

3D游戏画面

在开开心心的继续之前,是不是有记忆力好的人想起这个系列第一篇里面我说过,pygame不适合做3D,怎么这里又厚颜无耻的开始说3D了?这不是搬石头砸自己的脚么:)这里我要仔细说明一下,所谓3D,说到底就是利用透视原理,在2D的画面上创造出有纵深错觉(说白了也就是近大远小)的画面而已,毕竟,屏幕是平的,怎么可能真的画出距离呢?换句话说,计算机3D的本质还是2D,只不过额外多了很多东西。

在纯pygame中,我们画3D画面就是通过计算在2D图像上画一些大小不一的东西:)

距离的魔法

我们看现实中的东西,和我们看画面上的东西,最大差别在于能感受现实物体的距离。而距离的产生,则是因为我们双眼看到的东西是不同的,两眼交替闭合,你会发现眼前的东西左右移动。一只眼睛则很难正确的判断距离,虽然比上眼睛还是能感觉到远近,但更精细一点,比如很难把线穿过针眼。

我们在3D画面上绘图的时候,就要遵循这个规律,看看下面的代码。

import pygame
from pygame.locals import *
from random import randint

class Star(object):

    def __init__(self, x, y, speed):

        self.x = x
        self.y = y
        self.speed = speed

def run():

    pygame.init()
    screen = pygame.display.set_mode((640, 480)) #, FULLSCREEN)

    stars = []

    # 在第一帧,画上一些星星
    for n in xrange(200):

        x = float(randint(0, 639))
        y = float(randint(0, 479))
        speed = float(randint(10, 300))
        stars.append( Star(x, y, speed) )

    clock = pygame.time.Clock()

    white = (255, 255, 255)

    while True:

        for event in pygame.event.get():
            if event.type == QUIT:
                return
            if event.type == KEYDOWN:
                return

        # 增加一颗新的星星
        y = float(randint(0, 479))
        speed = float(randint(10, 300))
        star = Star(640., y, speed)
        stars.append(star)

        time_passed = clock.tick()
        time_passed_seconds = time_passed / 1000.

        screen.fill((0, 0, 0))

        # 绘制所有的星
        for star in stars:

            new_x = star.x - time_passed_seconds * star.speed
            pygame.draw.aaline(screen, white, (new_x, star.y), (star.x+1., star.y))
            star.x = new_x

        def on_screen(star):
            return star.x > 0

        # 星星跑出了画面,就删了它
        stars = filter(on_screen, stars)

        pygame.display.update()

if __name__ == "__main__":
    run()

这里你还可以把FULLSCREEN加上,更有感觉。

这个程序给我的画面,发挥一下你的想象,不是一片宇宙么,无数的星云穿梭,近的速度更快,远的则很慢。而实际上看代码,我们只是画了一些长短不同的线而已!虽然很简单,还是用了不少不少python的技术,特别是函数式编程的(小)技巧。不过强大的你一定没问题:)但是pygame的代码,没有任何没讲过的,为什么这样就能有3D的效果了?感谢你的大脑,因为它知道远的看起来更慢,所以这样的错觉就产生了。

理解3D空间

3D空间的事情,基本就是立体几何的问题,高中学一半应该就差不多理解了,这里不多讲了。你能明白下图的小球在(7, 5, 10)的位置,换句话说,如果你站在原点,面朝Z轴方向。那么小球就在你左边7,上面5,前面10的位置。这就够了~

3D坐标

使用3D向量

我们已经学习了二维向量来表达运动,在三维空间内,当然要使用三维的向量。其实和二维的概念都一样,加减缩放啥的,这里就不用三个元素的元组列表先演练一番了,直接祭出我们的gameobjects神器吧!

from gameobjects.vector3 import *
A = Vector3(6, 8, 12)
B = Vector3(10, 16, 12)
print "A is", A
print "B is", B
print "Magnitude of A is", A.get_magnitude()
print "A+B is", A+B
print "A-B is", A–B
print "A normalized is", A.get_normalized()
print "A*2 is", A * 2

运行一下看看结果吧,有些无趣?确实,光数字没法展现3D的美啊,下一次,让我们把物体在立体空间内运动起来。

>> 用Python和Pygame写游戏-从入门到精通(18)

22 thoughts on “用Python和Pygame写游戏-从入门到精通(17)

  1. zhangtl

    终于更新了哇 持续关注 Panda3D这个用来做3D好像还行吧 不过官方的文档比较少

    Reply
  2. xishui Post author

    @zhangtl: 哈,其实一开始我是打算写Panda3D的,不过能力有限啊,还是先从简单的开始……

    Reply
  3. Zhu

    距离的魔法。。。。。。。。。。。看到这个 不知道是不是就是广为流传的距离产生美??? o/
    很喜欢这类教程 希望版主继续更新啊

    Reply
  4. pygame

    那个星星的例子有问题啊,那个应该是pygame.QUIT和pygame.KEYDOWN,而不是QUIT和KEYDOWN

    Reply
    1. xx

      没问题啊,你看看是不是你导入的问题还是Python版本的问题

      Reply
  5. PulqueDelaRiddlehous

    python2的filter函数返回列表,到python3返回迭代器
    所以移植到3要将stars = filter(on_screen, stars)改为stars = list(filter(on_screen, stars))
    : )

    Reply
    1. 。。

      ……我用的是 stars = [x for x in filter(on_screen, stars)]
      不过话说3.6里面连xrange都没了,要改成range的

      Reply
  6. Pingback: 用Python和Pygame写游戏从入门到精通(17)-演道网

  7. pycjava

    那个在第一针加上星星的for循环里,那个xrange是什么?

    Reply
    1. 学习python

      那个是python2中的,python3中改为range()就可以了

      Reply
  8. mosey

    想知道,可不可以把一个屏幕里每一颗星星颜色都变为随机啊,我试了一下,只能做到每次运行的时候颜色随机,一个屏幕里的星星颜色还是一样的。。。。

    Reply
    1. JesmineH

      建立一个新类
      class Star2(object):
      def __init__(self, x, y, speed, color):
      self.x = x
      self.y = y
      self.speed = speed
      self.color = color

      Reply
  9. yu

    File “C:/Users/29119/PycharmProjects/firstpro/temp4.py”, line 43, in run
    stars = star.append(star)
    AttributeError: ‘Star’ object has no attribute ‘append’

    这是啥子回事 ? 怎么修改?

    Reply
  10. yu

    上面的错了 是这个
    File “C:/Users/29119/PycharmProjects/firstpro/temp4.py”, line 43, in run
    stars.append(star)
    AttributeError: ‘filter’ object has no attribute ‘append’

    忘告知

    Reply
    1. bloodish

      stars = filter(on_screen, stars)
      ==>
      stars = list(filter(on_screen, stars))

      Reply
  11. Pingback: 用 Python 和 Pygame 写游戏 – 从入门到精通(目录) – ITPCB

  12. jack_xy

    新版的python3.X好像不支持gameobject了,好在pygame.math自带Vector3功能,功能和函数大同小异,可以使用。具体大家看下帮助文档就可以了:
    from pygame.math import Vector3
    help( Vector3)

    Reply
  13. Grant7788

    在Python3下面会报错。需要修改gameobjects的代码。
    以下修改仅针对Windows 10 / Python 3.6测试过。
    找到安装gameobjects的目录,比如C:\Users\YOURNAME\.conda\envs\py36\Lib\site-packages\gameobjects
    所有文件中:
    1. print后面的内容加扩号
    2. Raise后面的内容加扩号
    3. from util import format_number更改为from gameobjects.util imiport format_number
    4. vector3.py
    line 18: self._v = map(float, args[:3])) 更改为:self._v = list(map(float, args[:3])))
    line 24: self._v = map(float, args[0][:3])) 更改为:self._v = list(map(float, args[0][:3])))

    Reply

发表评论

您的电子邮箱地址不会被公开。