Python利用3D引擎制作一个3D迷宫游戏

Ursina是一个3D引擎,初步使用方法,见文章:详解Python 3D引擎Ursina如何绘制立体图形

了解完Ursina的初步用法,接下来,我们就开始设计这个3D迷宫游戏啦!

效果:

墙面、地板、起始块、终点块所需要的图像资源我放在下面供大家下载

↓↓

brick.png

redstoneblock.jpg

greendiamondblock.jpg 

plank.jpg

代码详解:

首先,要用Prim最小生成树的方法生成迷宫,原理就是不断遍历还未遍历过的墙,并不断地删除不需要的墙块,代码见文章:Python Prim算法通过遍历墙实现迷宫的生成

这里,还有用遍历网格生成迷宫的方法,不过在编辑这个3D迷宫时,我们最好选用上方遍历墙的方式,顺便送上遍历网格的文章:Python利用Prim算法生成迷宫

把生成迷宫的代码命名为create.py

在主程序main.py中导入相关模块,其中ursina是整个3D引擎,ursina中有一个prefabs模块,prefabs中有一些可以直接拿出来用的东西,比如first_person_controller中的FirstPersonController,是用于设置第一人称的,sky中的Sky可以直接生成天空

然后,create模块中createMaze模块就是我们的Prim生成迷宫的算法

from ursina import *
from ursina.prefabs.first_person_controller import FirstPersonController
from ursina.prefabs.sky import Sky
from create import createMaze

接下来,创建Ursina主程序

app=Ursina()

把刚刚提供给大家的图片存到同目录下的texture文件夹,作为材质包

在程序中引入这些材质

wall_texture=load_texture("texture/brick.png")
start_texture=load_texture("texture/redstoneblock.jpg")
end_texture=load_texture("texture/greendiamondblock.jpg")
ground_texture=load_texture("texture/plank.jpg")

创建Wall类,墙壁方块,继承于Button类,注意,这里不要用Entity,Entity是没有碰撞体积的,所以要判断玩家是否落地,需要写更多代码,这里用Button类,后面使用第一人称时可以更方便,这里parent是场景,也就是Ursina里默认的scene,系统已经自动定义好了,model是cube,也就是正方体,texture即材质,position是坐标,坐标现在还未确定,所以在__init__中添加为参数,color设置为白色,这样贴材质的时候才不会让材质变色,设置为白色,可以让材质显示自己本身的颜色,Ursina中有color变量,一些常用的颜色可以这样使用:color.颜色名,或者用rgb的形式

class Wall(Button):
    def __init__(self,position):
        super().__init__(
            parent=scene,
            model="cube",
            texture=wall_texture,
            color=color.white,
            position=position,
            origin_y=0.5
        )

再创建一个类Start,用作起始方块,这里参数都差不多,只改了材质

class Start(Button):
    def __init__(self,position):
        super().__init__(
            parent=scene,
            model="cube",
            texture=start_texture,
            color=color.white,
            position=position,
            origin_y=0.5
        )

然后Ground(地板)和End(结束点)的类也差不多,都只改了个材质

class End(Button):
    def __init__(self,position):
        super().__init__(
            parent=scene,
            model="cube",
            texture=end_texture,
            color=color.white,
            position=position,
            origin_y=0.5
        )

class Ground(Button):
    def __init__(self,position):
        super().__init__(
            parent=scene,
            model="cube",
            texture=ground_texture,
            color=color.white,
            position=position,
            origin_y=0.5
        )

接下来是Player,我们这里不直接使用Ursina的FirstPersonController,因为它系统已经帮你设置好了行走速度、重力加速度、跳跃等等,这里在Pycharm编辑器里,ctrl+鼠标点FirstPersonController即可看到对应的FirstPersonController的系统代码,系统代码如下:

class FirstPersonController(Entity):
    def __init__(self, **kwargs):
        self.cursor = Entity(parent=camera.ui, model='quad', color=color.pink, scale=.008, rotation_z=45)
        super().__init__()
        self.speed = 5
        self.height = 2
        self.camera_pivot = Entity(parent=self, y=self.height)

        camera.parent = self.camera_pivot
        camera.position = (0,0,0)
        camera.rotation = (0,0,0)
        camera.fov = 90
        mouse.locked = True
        self.mouse_sensitivity = Vec2(40, 40)

        self.gravity = 1
        self.grounded = False
        self.jump_height = 2
        self.jump_up_duration = .5
        self.fall_after = .35 # will interrupt jump up
        self.jumping = False
        self.air_time = 0

        for key, value in kwargs.items():
            setattr(self, key ,value)

        # make sure we don't fall through the ground if we start inside it
        if self.gravity:
            ray = raycast(self.world_position+(0,self.height,0), self.down, ignore=(self,))
            if ray.hit:
                self.y = ray.world_point.y

    def update(self):
        self.rotation_y += mouse.velocity[0] * self.mouse_sensitivity[1]

        self.camera_pivot.rotation_x -= mouse.velocity[1] * self.mouse_sensitivity[0]
        self.camera_pivot.rotation_x= clamp(self.camera_pivot.rotation_x, -90, 90)

        self.direction = Vec3(
            self.forward * (held_keys['w'] - held_keys['s'])
            + self.right * (held_keys['d'] - held_keys['a'])
            ).normalized()

        feet_ray = raycast(self.position+Vec3(0,0.5,0), self.direction, ignore=(self,), distance=.5, debug=False)
        head_ray = raycast(self.position+Vec3(0,self.height-.1,0), self.direction, ignore=(self,), distance=.5, debug=False)
        if not feet_ray.hit and not head_ray.hit:
            self.position += self.direction * self.speed * time.dt

        if self.gravity:
            # gravity
            ray = raycast(self.world_position+(0,self.height,0), self.down, ignore=(self,))
            # ray = boxcast(self.world_position+(0,2,0), self.down, ignore=(self,))

            if ray.distance <= self.height+.1:
                if not self.grounded:
                    self.land()
                self.grounded = True
                # make sure it's not a wall and that the point is not too far up
                if ray.world_normal.y > .7 and ray.world_point.y - self.world_y < .5: # walk up slope
                    self.y = ray.world_point[1]
                return
            else:
                self.grounded = False

            # if not on ground and not on way up in jump, fall
            self.y -= min(self.air_time, ray.distance-.05) * time.dt * 100
            self.air_time += time.dt * .25 * self.gravity

    def input(self, key):
        if key == 'space':
            self.jump()

    def jump(self):
        if not self.grounded:
            return

        self.grounded = False
        self.animate_y(self.y+self.jump_height, self.jump_up_duration, resolution=int(1//time.dt), curve=curve.out_expo)
        invoke(self.start_fall, delay=self.fall_after)

    def start_fall(self):
        self.y_animator.pause()
        self.jumping = False

    def land(self):
        # print('land')
        self.air_time = 0
        self.grounded = True

    def on_enable(self):
        mouse.locked = True
        self.cursor.enabled = True

    def on_disable(self):
        mouse.locked = False
        self.cursor.enabled = False

我们只需要修改里面一些参数,__init__是一定要被修改的,跳跃的时候有时候会卡墙,然后跳到迷宫顶端,所以咱们这里禁用跳跃,重写jump函数,用pass代替即可。

gravity是重力,我们准备这样设计:一开始,我们是在半空中,然后缓缓下降,进入迷宫初始点,所以要缓缓下降就需要改到重力,将self.gravity改为0.01,也就是默认的1%,然后将玩家移动速度设置为6,也就是将self.speed设置为6,self.position是坐标,暂时未知,用全局变量代替,camera像scene和color一样,也是Ursina系统代码中已经帮我们定义好的了,我们无需重新定义,直接使用即可,camera中的fov是视角,咱们设置大一点,改为140,也差不多算广角了,然后设置self.mouse_sensitivity(鼠标敏感度),这个要像系统代码一样用Vec2,而且要传两个参数,我也不知道为啥这样,反正系统怎么写,咱们格式就尽量跟系统一样。其实这个灵敏度,就是鼠标移动视角的时候的速度,原来是40,40,这里我们设置为原来的4倍,160,160。

别忘了要执行父类的初始化函数哦!(super().__init__())

class Player(FirstPersonController):
    def __init__(self):
        global startPosition
        super().__init__()
        camera.fov=140
        self.position=startPosition
        self.gravity=0.01
        self.speed=6
        self.mouse_sensitivity=Vec2(160,160)

    def jump(self):
        pass

接下来,生成迷宫,然后定义参数,banPositions存储起始块和结束块的坐标,生成地板的时候要跳过,因为在这两个块的地板是与其他不同的

maze=createMaze(15,15)
startPosition=None
endPosition=None
banPostions=[]

这里,因为一个格宽度的路很窄,显得不宽敞,而且有时候行走也有些不方便,这里,我们把每个块放大为2x2,然后再创建场景

for y in range(1,4):
    for x,row in enumerate(maze):
        for z,value in enumerate(row):
            if str(value)=="s":
                Start((x*2,0,z*2))
                Start((x*2,0,z*2+1))
                Start((x*2+1,0,z*2))
                Start((x*2+1,0,z*2+1))
                startPosition=(x*2,3,z*2)
                banPostions.append([x*2,z*2])
                banPostions.append([x*2,z*2+1])
                banPostions.append([x*2+1,z*2])
                banPostions.append([x*2+1,z*2+1])
            elif str(value)=="e":
                End((x*2,0,z*2))
                End((x*2,0,z*2+1))
                End((x*2+1,0,z*2))
                End((x*2+1,0,z*2+1))
                endPosition=(x*2,3,z*2)
                banPostions.append([x*2,z*2])
                banPostions.append([x*2,z*2+1])
                banPostions.append([x*2+1,z*2])
                banPostions.append([x*2+1,z*2+1])
            elif str(value)=="0":
                Wall((x*2,y,z*2))
                Wall((x*2,y,z*2+1))
                Wall((x*2+1,y,z*2))
                Wall((x*2+1,y,z*2+1))

生成地板

y2=0
for x2 in range(x*2+1):
    for z2 in range(z*2+1):
        if not ([x2,z2] in banPostions):
            Ground((x2,y2,z2))

然后实例化player和sky,最后运行程序

player=Player()
sky=Sky()

app.run()

这样就可以实现文章一开始图片中的效果啦!

不过我们走到终点也没有啥效果,这个就留给大家自己尝试和拓展啦!在这里就不再讲解~

最后,附上main.py的参考代码(迷宫生成的代码到我文章开头给出的链接中查看复制):

from ursina import *
from ursina.prefabs.first_person_controller import FirstPersonController
from ursina.prefabs.sky import Sky
from create import createMaze

app=Ursina()

wall_texture=load_texture("texture/brick.png")
start_texture=load_texture("texture/redstoneblock.jpg")
end_texture=load_texture("texture/greendiamondblock.jpg")
ground_texture=load_texture("texture/plank.jpg")

class Wall(Button):
    def __init__(self,position):
        super().__init__(
            parent=scene,
            model="cube",
            texture=wall_texture,
            color=color.white,
            position=position,
            origin_y=0.5
        )

class Start(Button):
    def __init__(self,position):
        super().__init__(
            parent=scene,
            model="cube",
            texture=start_texture,
            color=color.white,
            position=position,
            origin_y=0.5
        )

class End(Button):
    def __init__(self,position):
        super().__init__(
            parent=scene,
            model="cube",
            texture=end_texture,
            color=color.white,
            position=position,
            origin_y=0.5
        )

class Ground(Button):
    def __init__(self,position):
        super().__init__(
            parent=scene,
            model="cube",
            texture=ground_texture,
            color=color.white,
            position=position,
            origin_y=0.5
        )

class Player(FirstPersonController):
    def __init__(self):
        global startPosition
        super().__init__()
        camera.fov=140
        self.position=startPosition
        self.gravity=0.01
        self.speed=6
        self.mouse_sensitivity=Vec2(160,160)

    def jump(self):
        pass

maze=createMaze(15,15)
startPosition=None
endPosition=None
banPostions=[]
for y in range(1,4):
    for x,row in enumerate(maze):
        for z,value in enumerate(row):
            if str(value)=="s":
                Start((x*2,0,z*2))
                Start((x*2,0,z*2+1))
                Start((x*2+1,0,z*2))
                Start((x*2+1,0,z*2+1))
                startPosition=(x*2,3,z*2)
                banPostions.append([x*2,z*2])
                banPostions.append([x*2,z*2+1])
                banPostions.append([x*2+1,z*2])
                banPostions.append([x*2+1,z*2+1])
            elif str(value)=="e":
                End((x*2,0,z*2))
                End((x*2,0,z*2+1))
                End((x*2+1,0,z*2))
                End((x*2+1,0,z*2+1))
                endPosition=(x*2,3,z*2)
                banPostions.append([x*2,z*2])
                banPostions.append([x*2,z*2+1])
                banPostions.append([x*2+1,z*2])
                banPostions.append([x*2+1,z*2+1])
            elif str(value)=="0":
                Wall((x*2,y,z*2))
                Wall((x*2,y,z*2+1))
                Wall((x*2+1,y,z*2))
                Wall((x*2+1,y,z*2+1))

y2=0
for x2 in range(x*2+1):
    for z2 in range(z*2+1):
        if not ([x2,z2] in banPostions):
            Ground((x2,y2,z2))

player=Player()
sky=Sky()

app.run()

以上就是Python利用3D引擎制作一个3D迷宫游戏的详细内容,更多关于Python 3D迷宫游戏的资料请关注我们其它相关文章!

(0)

相关推荐

  • 详解如何利用Python绘制迷宫小游戏

    目录 构思 绘制迷宫 走出迷宫 完整代码 更大的挑战 关于坐标系设置 周末在家,儿子闹着要玩游戏,让玩吧,不利于健康,不让玩吧,扛不住他折腾,于是想,不如一起搞个小游戏玩玩! 之前给他编过猜数字 和 掷骰子 游戏,现在已经没有吸引力了,就对他说:“我们来玩个迷宫游戏吧.” 果不其然,有了兴趣,于是和他一起设计实现起来,现在一起看看我们是怎么做的吧,说不定也能成为一个陪娃神器~ 先一睹为快: 构思 迷宫游戏,相对比较简单,设置好地图,然后用递归算法来寻找出口,并将过程显示出来,增强趣味性. 不如想

  • Python利用Prim算法生成迷宫

    Prim算法随机生成后的迷宫数列矩阵如下图: 15x15: 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 s 0 1 0 1 0 1 1 1 1 1 0 1 0 0 1 0 1 0 1 0 0 0 1 0 0 0 1 0 0 1 1 1 1 1 1 1 1 1 1 1 0 1 0 0 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 1 0 1 1 1 0 1 1 1 0 1 0 1 0 0 0 0 0 0 0 0 0 0 1 0 1 0 1 0 0 1 1 1 1

  • Python利用3D引擎写一个Pong游戏

    目录 前言 实现方法 完整代码 前言 之前,我们用pygame做了一个2D的Pong游戏,今天我们做一个3D的,游戏画面如下: 用ad和←→操作,双人对战 实现该效果我们使用Python强大的3D引擎Ursina,基础的使用方法见这篇文章:详解Python 3D引擎Ursina如何绘制立体图形 接下来开始写代码吧! 实现方法 首先,导入ursina和随机库 from ursina import * import random as rd 定义两个玩家的分数 scorea=scoreb=0 然后,

  • Python Prim算法通过遍历墙实现迷宫的生成

    之前,我们在另外一篇文章中使用Prim算法生成了一个完美迷宫,利用的是遍历网格的方法,这一次,我们要教教大家用遍历墙的方法生成,上一篇文章链接:Python利用Prim算法生成迷宫 我们需要用到随机库random,以及用来计算算法使用时间的time模块 导入这些模块 import random as rd import time 我们定义一个函数 def createMaze(a,b): # a:width b:height 添加一个变量储存算法开始的时间 startTime=time.time

  • 如何用 Python 制作一个迷宫游戏

    相信大家都玩过迷宫的游戏,对于简单的迷宫,我们可以一眼就看出通路,但是对于复杂的迷宫,可能要仔细寻找好久,甚至耗费数天,然后可能还要分别从入口和出口两头寻找才能找的到通路,甚至也可能找不到通路. 虽然走迷宫问题对于我们人类来讲比较复杂,但对于计算机来说却是很简单的问题.为什么这样说呢,因为看似复杂实则是有规可循的. 我们可以这么做,携带一根很长的绳子,从入口出发一直走,如果有岔路口就走最左边的岔口,直到走到死胡同或者找到出路.如果是死胡同则退回上一个岔路口,我们称之为岔口 A, 这时进入左边第二

  • 详解Python 3D引擎Ursina如何绘制立体图形

    目录 编写第一个程序 绘制实体长方体 对实体进行同样比例的放大和缩小 对实体进行任意放大 绘制球体 为你的实体上色 Python有一个不错的3D引擎——Ursina Ursina官网:www.ursinaengine.org 打开cmd,控制台输入pip install ursina以安装ursina 编写第一个程序 首先导入ursina from ursina import * 然后创建app app=Ursina() 运行app app.run() 最终代码: from ursina imp

  • Python利用3D引擎做一个太阳系行星模拟器

    这次,我们再来用Ursina引擎来做一个太阳系行星模拟器吧! 想要了解Ursina 3D引擎的基本使用方法的话,查看我的另一篇文章:详解Python 3D引擎Ursina如何绘制立体图形 这一次,我们要实现的效果如下 首先,送上本次需要用到的资源 Earth.jpg Jupiter.jpg Mars.jpg Mercury.jpg Neptune.jpg Saturn.jpg Sun.jpg Uranus.jpg Venus.jpg 现在,就开始写代码吧! 首先,导入我们需要的模块,导入3D引擎

  • 基于Three.js制作一个3D中国地图

    目录 1.使用geoJson绘制3d地图 1.1 创建场景相关 1.2 根据json绘制地图 2.增加光照 3.增加阴影模糊 4.增加鼠标事件 5.渲染 6.动画效果 不想看繁琐步骤的,可以直接去github下载项目,如果可以顺便来个star哈哈 本项目使用vue-cli创建,但不影响使用,主要绘制都已封装成类 1.使用geoJson绘制3d地图 1.1 创建场景相关 // 创建webGL渲染器 this.renderer = new THREE.WebGLRenderer( { antiali

  • 使用Three.js制作一个3D奖牌页面

    目录 背景 效果 实现 引入资源 场景初始化 光照效果 Three.js 提供的光源 添加网格和地面 创建奖牌 奖牌UI素材生成 Three.js 中的贴图 MeshPhysicalMaterial 物理材质 特殊属性 加载1000+文字模型 补间动画 动画更新 礼花动画 总结 背景 本文使用 React + Three.js 技术栈,实现粉丝突破1000的3D纪念页面,包含的主要知识点包括:Three.js 提供的光源.DirectionLight 平行光.HemisphereLight 半球

  • Python利用物理引擎Pymunk编写一个解压小游戏

    用鼠标创建小球,一个蹦来蹦去的解压小游戏…… 本次需要的外置包:pygame,pymunk,cmd运行该命令安装: pip install pygame pip install pymunk 首先,导入 import pymunk import pygame from pygame.locals import * import sys import random as rd 结合pygame,创建若干障碍,并设置重力.弹跳力等参数  class Demo: WIDTH=800 HEIGHT=80

  • 教你使用html+css制作一个3D立体相册

    目录 前言 一.先看效果 二.实现步骤 总结 前言 用css的transform属性做一个3D相册~~~值translate表示偏移:scale表示缩放:rotate就是转动. 一.先看效果 二.实现步骤 1.我们知道一个正方体有6个面,所以定义一个父亲元素然后定义6个子元素作为6个面.每个面放一张图片.里面q1表示前面,h2表示后面,以此类推,就是首拼音. <div class="baba"> <div class="q1"> <im

  • Python利用matplotlib实现制作动态条形图

    目录 制作思路 animation 大家好,本文将分享如何使用matplotlib制作动态条形图,制作的图很美,这个是我在之前发布的一篇中使用的图片, 效果如下 制作思路 为了方便大家学习,我将不直接进行讲解,而是以我是如何一步步制作的思路来介绍整个过程. 说到用 Python 制作动态图,首先想到的肯定是一些直接拿来就用的库,虽然我没做过,但是我相信一定有且不止一个,搜了一圈后发现有个bar chart race库看起来不错 毕竟有现成的轮子,只需要填充数据即可,但是研究了一番,正如我之前所说

  • Python利用reportlab实现制作pdf报告

    目录 前言 reportlab是什么 安装和导入库 将画图.画表格.编辑文字抽象为类 pdf插入图片 以文件路径写入pdf 以流文件写入pdf pdf分页 以生成pdf流文件为例 前言 本博客重点内容:reportlab生成流文件格式.reportlab分页和图片流文件写入reportlab等. 我讲一下我这个需求的来源,做的项目是一个地理空间查询和使用的系统,通过在前端调用高德地图api创建了一个查询区域,获取区域内的地理数据(数据库).具体的需求就是,将查询区域和地理数据制作成一个覆盖率分析

  • Python利用pynimate实现制作动态排序图

    数据可视化动画还在用 Excel 做?今天分享一个简单的 Python 包就能分分钟搞定! 而且生成的动画也足够丝滑,效果是酱紫的: 这是一位专攻 Python 语言的程序员开发的安装包,名叫Pynimate. 目前可以直接通过PyPI安装使用. 使用指南 想要使用 Pynimate,直接import一下就行. import pynimate as nim 输入数据后,Pynimate将使用函数Barplot()来创建条形数据动画. 而创建这种动画,输入的数据必须是pandas数据结构(如下),

  • Python利用Turtle库绘制一个小老虎

    目录 导语 1.定义库以及初始化界面 2.画出左右两只耳朵 3.画出小老虎头部轮廓 4. 画出老虎的两只眼睛 5.画出老虎的鼻子和嘴巴 6.画出小老虎的左右肢体和脚趾 7.在需要的位置写上我们的新年祝福 8. 显示倒数3,2,1 9.显示我们需要的文字 10.设定代码运行入口,调用目标函数 成果展示 导语 哈喽铁汁们好久不见吖~小编已经复工了于是马不停蹄赶来给大家准备新年礼物算开工礼物吧! 海龟来作图 虎年就是要画老虎 2022不用纸和笔~ 今晚画老虎~ 一二三四五 老虎宝宝示意图 虎年怎么能少

随机推荐