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

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

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

(0)
上一篇 2022年5月26日 上午7:40
下一篇 2022年5月26日 上午7:40


相关推荐

  • 塑料高分子应用计算机,分子模拟方法与模拟软件Materials+Studio在高分子材料中的应用.pdf…

    塑料高分子应用计算机,分子模拟方法与模拟软件Materials+Studio在高分子材料中的应用.pdf…塑料庄吕清等——分了模拟方法及模拟软件MaterialsStudio在高分了材料中的应用2010年39卷第4期嵛誓。o。tjl:?、‘;’j1恭。j、t?lt.j一、o.、0分子模拟方法及模拟软件MaterialsStudio在高分子材料中的应用’庄昌清。岳红。张慧军(西北工业大学理学院应用化学系,陕西,西安710129)摘要:综述分子模拟的发…

    2022年5月25日
    46
  • excel如何从字符串中截取指定字符(LEFT、RIGHR、MID三大函数)

    excel如何从字符串中截取指定字符(LEFT、RIGHR、MID三大函数)目录 1 LEFT 函数 2 RIGHT 函数 3 MID 函数 1 LEFT 函数 1 语法 LEFT text num chars 参数 text 必要参数 包含要提取字符的文本字符串 字符串表达式其中最左边的那些字符将被返回 如果 text 包含 Null 将返回 Null num chars 可选参数 数值表达式 指出将返回多少个字符 num chars 必须大于或等于 0 如果省略第二参数 则假设其值为 1 如果为 0 返回零长度字符串 如果大于或等于

    2026年3月18日
    2
  • 用计算机最炫民族风乐谱,最炫民族风简谱「建议收藏」

    用计算机最炫民族风乐谱,最炫民族风简谱「建议收藏」最炫民族风苍茫的天涯是我的爱绵绵的青山脚下花正开什么样的节奏是最呀最摇摆什么样的歌声才是最开怀弯弯的河水从天上来流向那万紫千红一片海火辣辣的歌谣是我们的期待一路边走边唱才是最自在我们要唱就要唱得最痛快你是我天边最美的云彩让我用心把你留下来(留下来)悠悠的唱着最炫的民族风让爱卷走所有的尘埃(我知道)你是我心中最美的云彩斟满美酒让你留下来(留下来)永远都唱着最炫的民族风是整片天空最美的姿态(留下…

    2026年2月20日
    7
  • JavaScript闭包定义及原理

    JavaScript闭包定义及原理br 对于初学者来说 理解 Javascript 闭包 closure 还是比较困难的 而撰写此文的目的就是用最通俗的文字揭开 Javascript 闭包的真实面目 让初学者理解起来更加容易一些 br 一 什么是闭包 br 官方 的解释是 闭包是一个拥有许多变量和绑定了这些变量的环境的表达式 通常是一个函数 因而这些变量也是该表达式的一部分 相信很少有人能直接看懂这句话 因为他描述的太学术 br 其实这句话通俗的来说就是 JavaScript 中所有的 function 都是一个闭包 不过一

    2026年3月18日
    2
  • json_decode的结果是null

    json_decode的结果是null一、前言      突然发现一个接口出了问题,经过排查之后发现是json_decode($str,true)的问题,返回竟然是null。这个问题大家可能都碰到过,出现问题的原因就那么几种,再次记录一下吧二、原因1、首先使用json_last_error确定问题$arrDataList=json_decode($content…

    2022年7月17日
    21
  • 基于回归分析的房价预测模型_房价模型基本原理

    基于回归分析的房价预测模型_房价模型基本原理回归模型与房价预测

    2022年4月21日
    112

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

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