首页 > 编程知识 正文

榜样6学习笔记,学习游戏

时间:2023-05-03 07:42:59 阅读:218500 作者:2870

    pygame是是上世纪的产品,虽然不适合最3D游戏,但我可以使用pygame来绘制简单的3D图形,就像在白纸上画立体图形一样。

主要内容: 视觉上的远近、3D空间、绘制一个空间图形


一、视觉上的远近

  人的视觉总是遵循一个原则:近大远小。想象一下,如果有一天躺在草地上什么都不做,仰望星空,天空突然飞过流星雨,离我们近的流星看起来就很狭长而明亮,离我们远的则呈一个暗点状,我们完全可以用pygame模拟出来:

step1:定义Star类

  要描述一颗流星,需要有坐标和速度。在定义一个列表来存储点集:

class Star(object):def __init__(self, x, y, speed):self.x = xself.y = yself.speed = speedstars = []

step2:生成随机点
  我们需要定时的生成流星

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

step3:绘制短线
  因为近的流星彗尾看起来更狭长,所以我们可以根据速度来绘制一条短线来表示流星。可以使用pygaame的time库函数Clock测量时间,根据时间和速度在不同位置绘制不同长度的短线:

while True:for star in stars:new_x = star.x - star.speed*time_passed_secondspygame.draw.aaline(screen, white, (new_x, star.y), (star.x+1, star.y))star.x = new_x

step4:查看效果

import pygamefrom pygame.locals import *from random import randintclass Star(object):def __init__(self, x, y, speed):self.x = xself.y = yself.speed = speeddef run():pygame.init()screen = pygame.display.set_mode((640, 480), 0, 32)stars = []for k in range(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:pygame.quit()returnelif event.type == KEYDOWN:returny = float(randint(0, 479))speed = float(randint(10, 300))stars.append(Star(640., y, speed))time_passed = clock.tick(60)time_passed_seconds = time_passed / 1000screen.fill((0, 0, 0))for star in stars:new_x = star.x - star.speed*time_passed_secondspygame.draw.aaline(screen, white, (new_x, star.y), (star.x+1, star.y))star.x = new_xdef on_screen(star):return star.x > 0stars = list(filter(on_screen, stars))pygame.display.update()if __name__ == "__main__":run()

  这里我们需要注意一个细节,我们定义了一个on_screen函数,若一个点在屏幕上则返回值为True,使用filter函数将不在屏幕的函数过滤掉,而保留屏幕上的点(不在屏幕上的点会消耗资源)。

def on_screen(star):return star.x > 0stars = list(filter(on_screen, stars))

  我们不断生成长短不一运动的短线,看起来就像流星一样✨



二、3D空间

  首先,我们定义我们的坐标系,z粥朝向我们,x轴朝右,就和pygame窗口的x轴一样,y轴朝上,和pygame窗口的y轴方向相反。

投影:平行投影
  投影最简单的办法——丢弃z坐标

def parallel_project(vector3):return(vector3.x, vector3.y)

  这种方法简单,但得到的效果很不理想,基本上看不出透视效果。

投影:立体投影
  这种投影的效果更接近真实,在它利用透视法把远处的物体缩小了。游戏中广泛应用。

def perspective_project(vector3, d): x, y, z = vector3 return (x*d/z, -y*d/z)

  由于近大远小,所以我们的结果除以一个z,同时,这里考虑了视距d(观察者到屏幕的距离),如下图。
  类比:可以想象通过一个孔观察事物,当眼睛远离孔(视距增大)时,视野(fov)减小,看到的内容自然就变少了,当眼睛靠近孔时,视距减小,视野增大,看到的内容增多了。
  类比:可以把z看作观察者到被观测物体之间的观测距离,可以看成照相机到物体的距离,当距离z增大时,看到的物体变小,符合透视原理。


视距:计算视距

from math import tandef calculate_viewing_distance(fov, screen_width):d = (screen_width/2.0) / tan(fov/2.0)return d

三、绘制一个空间图形

  我们可以根据上面所学的,结合数学基础,可以绘制一个3D图形,关键代码如下:

# 绘制点 for point in points: x, y, z = point - camera_position x = x * viewing_distance / z y = -y * viewing_distance / z x += center_x y += center_y screen.blit(ball, (x-ball_center_x, y-ball_center_y))

  在这里,我们先定义了一个相机的位置camera_position,它代表了观测者的位置,初始值为(0, 0, -700),因为要除以距离z,所以我们先将z设的比较大(因为如果z很小,如接近0,则(x, y)就接近(∞, ∞)了),之后我们将(x, y)加上center_y, center_y,目的是吧图形绘制在屏幕中央,最后根据ball的尺寸将球绘制在对于的位置。

  完整代码如下:

import pygamefrom pygame.locals import *from gameobjects.vector3 import Vector3from math import *from random import randintSCREEN_SIZE = (640, 480)CUBE_SIZE = 300def caculate_viewing_distance(fov, screen_width): d = (screen_width/2.0) / tan(fov/2.0) return ddef run(): pygame.init() screen = pygame.display.set_mode(SCREEN_SIZE, 0, 32) my_font = pygame.font.SysFont("arial", 23) ball = pygame.image.load("ball.png") # 3D points points = [] fov = 90.0 # Field of view viewing_distance = caculate_viewing_distance(radians(fov), SCREEN_SIZE[0]) # 正方体边上的点,每条边16个点 for x in range(0, CUBE_SIZE+1, 20): edge_x = (x == 0 or x == CUBE_SIZE) for y in range(0, CUBE_SIZE+1, 20): edge_y = (y ==0 or y == CUBE_SIZE) for z in range(0, CUBE_SIZE+1, 20): edge_z = (z == 0 or z == CUBE_SIZE) # 以(0, 0, 0)为顶点的正方体边的坐标性质 if sum((edge_x, edge_y, edge_z)) >= 2: # 移动到以(0, 0, 0)为中心 point_x = float(x) - CUBE_SIZE/2 point_y = float(y) - CUBE_SIZE/2 point_z = float(z) - CUBE_SIZE/2 point = Vector3(point_x, point_y, point_z) points.append(point) # 以point_z从大到小排序 def point_z(point): return point.z points.sort(key=point_z, reverse=True) center_x, center_y = SCREEN_SIZE center_x /= 2 center_y /= 2 ball_w, ball_h = ball.get_size() ball_center_x = ball_w / 2 ball_center_y = ball_h / 2 camera_position = Vector3(0.0, 0.0, -700.0) camara_speed = Vector3(300.0, 300.0, 300.0) clock = pygame.time.Clock() while True: for event in pygame.event.get(): if event.type == QUIT: return screen.fill((0, 0, 0)) pressed_keys = pygame.key.get_pressed() time_passed = clock.tick() time_pass_seconds = time_passed / 1000.0 direction = Vector3() if pressed_keys[K_LEFT]: direction.x = -1.0 elif pressed_keys[K_RIGHT]: direction.x = +1.0 if pressed_keys[K_UP]: direction.y = +1.0 elif pressed_keys[K_DOWN]: direction.y = -1.0 if pressed_keys[K_q]: direction.z = +1.0 elif pressed_keys[K_a]: direction.z = -1.0 # fov大小有范围 0-179 if pressed_keys[K_w]: fov = min(179.0, fov+0.2) w = SCREEN_SIZE[0] viewing_distance = caculate_viewing_distance(radians(fov), w) elif pressed_keys[K_s]: fov = max(1.0, fov-0.2) w = SCREEN_SIZE[0] viewing_distance = caculate_viewing_distance(radians(fov), w) camera_position += direction * camara_speed * time_pass_seconds # 绘制点 for point in points: x, y, z = point - camera_position x = x * viewing_distance / z y = -y * viewing_distance / z x += center_x y += center_y screen.blit(ball, (x-ball_center_x, y-ball_center_y)) # 绘制表 diagram_width = SCREEN_SIZE[0] / 4 col = (50, 255, 50) diagram_points = [] diagram_points.append((diagram_width/2, 100+viewing_distance/4)) diagram_points.append( (0, 100) ) diagram_points.append( (diagram_width, 100) ) diagram_points.append( (diagram_width/2, 100+viewing_distance/4) ) diagram_points.append( (diagram_width/2, 100) ) pygame.draw.lines(screen, col, False, diagram_points, 2) # 绘制文字 white = (255, 255, 255) cam_text = my_font.render("camera = "+str(camera_position), True, white) screen.blit(cam_text, (5, 5)) fov_text = my_font.render("field of view = %i"%int(fov), True, white) screen.blit(fov_text, (5, 35)) txt = "viewing distance = %.3f"%viewing_distance d_text = my_font.render(txt, True, white) screen.blit(d_text, (5, 65)) pygame.display.update()if __name__ == "__main__": run()

下面就是我们最终的效果啦:

第 6 篇pygame学习笔记完结 cheers! ?

参考博客:用Python和Pygame写游戏-从入门到精通
图标引用:Iconfont

text-align软件

版权声明:该文观点仅代表作者本人。处理文章:请发送邮件至 三1五14八八95#扣扣.com 举报,一经查实,本站将立刻删除。