Pygame学习笔记 6 —— 3D游戏

Pygame学习笔记 6 —— 3D游戏    pygame是是上世纪的产品,虽然不适合最3D游戏,但我可以使用pygame来绘制简单的3D图形,就像在白纸上画立体图形一样。主要内容:视觉上的远近、3D空间基本知识、绘制一个空间图形…

大家好,又见面了,我是你们的朋友全栈君。

    pygame是是上世纪的产品,虽然不适合最3D游戏,但我可以使用pygame来绘制简单的3D图形,就像在白纸上画立体图形一样。
在这里插入图片描述
主要内容: 视觉上的远近、3D空间、绘制一个空间图形


一、视觉上的远近

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

step1:定义Star类

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

class Star(object):
	def __init__(self, x, y, speed):
		self.x = x
		self.y = y
		self.speed = speed

stars = []

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_seconds
		pygame.draw.aaline(screen, white, (new_x, star.y), (star.x+1, star.y))
		star.x = new_x

step4:查看效果

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), 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()
return
elif event.type == KEYDOWN:
return
y = 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 / 1000
screen.fill((0, 0, 0))
for star in stars:
new_x = star.x - star.speed*time_passed_seconds
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 = list(filter(on_screen, stars))
pygame.display.update()
if __name__ == "__main__":
run()

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

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

  我们不断生成长短不一运动的短线,看起来就像流星一样✨
Pygame学习笔记 6 —— 3D游戏


二、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 tan
def 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 pygame
from pygame.locals import *
from gameobjects.vector3 import Vector3
from math import *
from random import randint
SCREEN_SIZE = (640, 480)
CUBE_SIZE = 300
def caculate_viewing_distance(fov, screen_width):
d = (screen_width/2.0) / tan(fov/2.0)
return d
def 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

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

发布者:全栈程序员-用户IM,转载请注明出处:https://javaforall.cn/141355.html原文链接:https://javaforall.cn

【正版授权,激活自己账号】: Jetbrains全家桶Ide使用,1年售后保障,每天仅需1毛

【官方授权 正版激活】: 官方授权 正版激活 支持Jetbrains家族下所有IDE 使用个人JB账号...

(0)
blank

相关推荐

  • lscpu命令详解

    lscpu的使用描述:  此命令用来显示cpu的相关信息  lscpu从sysfs和/proc/cpuinfo收集cpu体系结构信息,命令的输出比较易读  命令输出的信息包含cpu数量,线程,核数,套接字和Nom-UniformMemeorAccess(NUMA),缓存等  不是所有的列都支持所有的架构,如果指定了不支持的列,那么lscpu将打印列,但不显示数据语法:

  • ideavim怎么用_idea基本使用教程

    ideavim怎么用_idea基本使用教程ideavim使用分享ideavim使用ideavim介绍ideavim是JetBrains官方开发的模拟vim插件,熟练ideavim的人可以更快的进行操作,大部分操作都可以用键盘来代替。纯vim也能进行更高效的开发,但是一款适合自己深定义的配置,能够让人更加高效。配合ide的智能补全,就一个字爽ideavim的安装idea中自带的插件管理搜索ideavim然后选择安装macos打开idea配置cmd+,windows打开idea配置ctrl+shfit+s

  • docker快速安装fastdfs服务springboot访问

    docker快速安装fastdfs服务springboot访问拉取镜像dockerpullmorunchang/fastdfs运行tracker跟踪器dockerrun-d–nametracker–net=hostmorunchang/fastdfsshtracker.sh运行storage存储器【注意:修改IP为自己的IP端口不变】dockerrun-d–namestorage–net=host-eTRACKER_IP=192.168.61.200:22122-eGROUP_NAME=gr

  • ATA考试注意事项「建议收藏」

    ATA考试注意事项「建议收藏」一、考试前将所有计算机除掉还原卡及还原软件。二、officeXp安装要用完全安装。三、服务器端尽量不要刷新所有客户端否则引起考试管理系统死机。四、拍照功能无法使用,可重新启动考试管理系统。五、服务器端无法扫描到客户端,除了服务器与客户端必须在同一网段内,可看一下客户端是否启动llistening    …

  • Latex 希腊字母大写[通俗易懂]

    Latex 希腊字母大写[通俗易懂]大写小写LaTeX大写大写LaTeX$A\quad\alpha$A\alphaNνN\quad\nuNνN\nuBβB\quad\betaBβB\betaΞξ\Xi\quad\xiΞξ\Xi\xiΓγ\Gamma\quad\gammaΓγ\Gamma\gammaOoO\quadoOoOoΔδ\Delta\quad\deltaΔδ\Delta\deltaΠπ\Pi\quad\piΠπ\Pi\p…

    2022年10月13日
  • navicat12注册机生成相应注册码

    navicat12注册机生成相应注册码转自:http://blog.sina.com.cn/s/blog_1885d23df0102yjw9.html navicat注册机是针对nacicat公司开发的软件而制作的一款激活工具,能够生成相对应的注册码和激活码,不仅能够支持激活nacicat11系列软件,同时可以激活nacicat12系列软件,具体可以激活navicat12formysql、navicat12fo…

    2022年10月13日

发表回复

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

关注全栈程序员社区公众号