ursina是Python的一个强大的3d模块。用了它,我们可以在里面“复刻“我的世界,并制作跑酷赛道。
第一步:pip安装ursina模块:
pip install ursina
因为模块有点大(实际包含Panda3d等几个模块),为提升下载速度,我们可以设置国内镜像源:
pip install ursina -i https://pypi.tuna.tsinghua.edu.cn/simple
这里用的是清华大学的,可自己选择源
然后写代码:
第一步:引用ursina模块和第一人称
from ursina import *
from ursina.prefabs.first_person_controller import FirstPersonController
第二步,创建Ursina程序并赋值纹理(图在后面),为下面代码做铺垫
grass_texture = load_texture('texture\grass')
stone_texture = load_texture('texture\stone')
brick_texture = load_texture(r'texture\brick')
dirt_texture = load_texture('texture\dirt')
sky_texture = load_texture('sky')
arm_texture = load_texture('white_cube')
bedrock_texture=load_texture(r"texture\bedrock")
punch_sound = Audio('sine',loop = False, autoplay = False)
block_pick = 1
block_texture_dict={
1:grass_texture,2:stone_texture,3:brick_texture,4:dirt_texture,5:'white_cube'}
diction={}
grass,stone,brick,dirt和bedrock_texture是方块的纹理,load_texture(r'texture\brick')表示打开ursina模块中textures文件夹中texture文件夹中的brick图片(格式jpg和png都行),而sky和arm的纹理图片是ursina自带的,不用下载。Audio('sine',loop=False,autoplay=False)表示打开ursina自带的音频sine.wav,因其声音像挖放方块的声音,为后面的代码做铺垫。loop=False即循环为假,autoplay=False即自动播放为假。block_texture_dict,diction是为方便下文的字典。
接下来是定义方块的类,继承Button类,因为Entity是没有碰撞体积的
class Voxel(Button):
def __init__(self, position = (0,0,0), texture = grass_texture,scale_y=1):
super().__init__(
parent = scene,
position = position,
model = 'cube',
origin_y = 0.5,
texture = texture,
color = color.color(0,0,random.uniform(0.9,1)),
scale_y = scale_y)
def update(self):
sx=self.x
sy=self.y
sz=self.z
if ((self.x - player.x) ** 2 + (self.y - player.y) ** 2 + (self.z - player.z) ** 2) ** 0.5>=10: #远离玩家10格以外的方块将被破坏
destroy(self)
try:
del diction[(sx,sy,sz)] #not exist any more
except KeyError:
pass
def input(self,key):
if self.hovered:
if ((self.x - player.x) ** 2 + (self.y - player.y) ** 2 + (self.z - player.z) ** 2) ** 0.5 <= 7: #distance #7格的手长
if key == 'right mouse down':
if block_pick!=5: punch_sound.play()
if block_pick == 1: voxel = Voxel(position = self.position + mouse.normal, texture = grass_texture)
if block_pick == 2: voxel = Voxel(position = self.position + mouse.normal, texture = stone_texture)
if block_pick == 3: voxel = Voxel(position = self.position + mouse.normal, texture = brick_texture)
if block_pick == 4: voxel = Voxel(position = self.position + mouse.normal, texture = dirt_texture)
if key == 'left mouse down':
if ((self.x-player.x)**2+(self.y-player.y)**2+(self.z-player.z)**2)**0.5<=7:
punch_sound.play()
if self.texture!=bedrock_texture: #基岩不能破坏
destroy(self)
因为是跑酷,赛道肯定很长,方块数量多了,电脑就危险了,所以我在update()中规定:离玩家距离超过10格的方块(if ((self.x - player.x) ** 2 + (self.y - player.y) ** 2 + (self.z - player.z) ** 2) ** 0.5>=10,后面我把玩家起名为plyer)会被自动清除(destory(self)),并把字典中此方块的位置清除。是的,diction是用于放置已存在的方块的位置的,具体如下:
diction={已存在方块位置:True}。为什么要这样呢?
离玩家距离超过10格的方块自动清除,那么10格以内的方块就要重新放置。但ursina中一个位置是允许有多个方块的,这会导致同位置有无数的方块,电脑同样很卡。所以每当生成一个方块,就把当前位置标记为True,当前位置不再生成方块。同样,当方块被清除,那么此位置的字典就被删除。为什么用True呢?便于后面的if语句。
还有,这里讲一下update()和input()的区别。update(self)用于数据的更新,每一帧都会运行;而input(self,key)函数只有在键盘、鼠标活动时才运行。他们两个会被ursina自动调用,我们只用定义就好了。并且由于我糟糕的跑酷技术,我设置了可以放置(input(self)函数中的if key == 'right mouse down':下的内容)和破坏方块(destory(self)),且加了if ((self.x - player.x) ** 2 + (self.y - player.y) ** 2 + (self.z - player.z) ** 2) ** 0.5 <= 7的限制,我们只够得着7格以内的方块。在左\右键时,播放sine.wav(punch_sound.play())模拟方块破坏声。 if self.texture!=bedrock_texture:是指不能破坏基岩
接下来便是定义天和手的类了:
class Sky(Entity):
def __init__(self):
super().__init__(
parent = scene,
model = 'sphere',
texture = sky_texture,
scale = 150,
double_sided = True)
class Hand(Entity):
def __init__(self,texture=grass_texture,scale_x=0.5,scale_y=0.5):
super().__init__(
parent = camera.ui,
model = 'cube',
texture = texture,
scale_x = scale_x,
scale_y = scale_y,
rotation = Vec3(150,-10,0),
position = Vec2(0.4,-0.6))
def update(self):
if block_pick==5:
self.scale_x=0.2
self.scale_y=0.2 #空手时
else:
self.scale_x=self.scale_y=0.5
self.texture=block_texture_dict[block_pick] #手持方块时
def active(self):
self.position = Vec2(0.2,-0.4)
def passive(self):
self.position = Vec2(0.4,-0.6)
这里sky的double_sidede一定要设成True,不然就是黑乎乎一片,其他的非常简单,就不用我讲了
然后便是一些设置和实例:
def input(key):
if key == 'q':
player.y+=100 #作弊键
if key == 'escape':
quit()
window.fullscreen = True
player=FirstPersonController()
text=Text(text=f"Position:({player.x},{player.y},{player.z})",position=(-0.85,0.5))
text2=Text(text='Die-times:',color=color.red,position=(-0.85,0.45))
sky = Sky()
hand = Hand() #创造一系列实例
zhi_music = Audio(r"zhi.mp3", autoplay=False, loop=False)
back_music = Audio(r"