用户:
Rtssts查看:0 回复:0 评论:0 创建时间:2023-03-26T12:03:18
【作品展示】

【作品介绍】
w,a,s,d
【作品源代码】
from __future__ import division
import sys
import math
import time
import random
from collections import deque
from pyglet import image
from pyglet.gl import*
from pyglet.graphics import TextureGroup
from pyglet.window import key,mouse
TICKS_PER_SEC=60
SECTOR_SIZE=2000
WALKING_SPEED=5
FLYING_SPEED=15
GRAVITY=20
MAX_JUMP_HEIGHT=1
JUMP_SPEED=math.sqrt(2*GRAVITY*MAX_JUMP_HEIGHT)
TERMINAL_VELOCITY=50
PLAYER_HEIGHT=2
if sys.version_info[0]>=3:
xrange=range
def cube_vertices(x,y,z,n):
""" Return the vertices of the cube at position x,y,z with size 2*n.
"""
return[
x-n,y+n,z-n,x-n,y+n,z+n,x+n,y+n,z+n,x+n,y+n,z-n,
x-n,y-n,z-n,x+n,y-n,z-n,x+n,y-n,z+n,x-n,y-n,z+n,
x-n,y-n,z-n,x-n,y-n,z+n,x-n,y+n,z+n,x-n,y+n,z-n,
x+n,y-n,z+n,x+n,y-n,z-n,x+n,y+n,z-n,x+n,y+n,z+n,
x-n,y-n,z+n,x+n,y-n,z+n,x+n,y+n,z+n,x-n,y+n,z+n,
x+n,y-n,z-n,x-n,y-n,z-n,x-n,y+n,z-n,x+n,y+n,z-n,
]
def tex_coord(x,y,n=4):
"""
Return the bounding vertices of the texture square.
"""
m=1.00/n
dx=x*m
dy=y*m
return dx,dy,dx+m,dy,dx+m,dy+m,dx,dy+m
def tex_coords(top,bottom,side):
"""
Return a list of the texture squares for the top,bottom and side.
"""
top=tex_coord(*top)
bottom=tex_coord(*bottom)
side=tex_coord(*side)
result=[]
result.extend(top)
result.extend(bottom)
result.extend(side*4)
return result
TEXTURE_PATH='texture.png'
GRASS=tex_coords((1,0),(0,1),(0,0))
SAND=tex_coords((1,1),(1,1),(1,1))
BRICK=tex_coords((2,0),(2,0),(2,0))
STONE=tex_coords((2,1),(2,1),(2,1))
PURPLE=tex_coords((3,1),(3,1),(3,1))
RED=tex_coords((3,0),(3,0),(3,0))
GREEN=tex_coords((3,2),(3,2),(3,2))
BLUE=tex_coords((1,2),(1,2),(1,2))
BLACK=tex_coords((2,2),(2,2),(2,2))
ORANGE=tex_coords((3,2),(3,2),(3,2))
FACES=[
(0,1,0),
(0,1,0),
(-1,0,0),
(1,0,0),
(0,0,1),
(0,0,-1),
]
def normalize(position):
""" Accepts `position` of arbitrary precision and returns the block
containing that position.
Parameters
----------
position:tuple of len 3
Returns
-------
block_position:tuple of ints of len 3
"""
x,y,z=position
x,y,z=(int(round(x)),int(round(y)),int(round(z)))
return(x,y,z)
def sectorize(position):
""" Returns a tuple representing the sector for the given `position`.
Parameters
----------
position:tuple of len 3
Returns
-------
sector:tuple of len 3
"""
x,y,z=normalize(position)
x,y,z=x//SECTOR_SIZE,y//SECTOR_SIZE,z//SECTOR_SIZE
return(x,0,z)
class Model(object):
def __init__(self):
self.batch=pyglet.graphics.Batch()
self.group=TextureGroup(image.load(TEXTURE_PATH).get_texture())
self.world={}
self.shown={}
self._shown={}
self.sectors={}
self.queue=deque()
self._initialize()
def _initialize(self):
""" Initialize the world by placing all the blocks.
"""
n=200
s=1
y=0
for x in xrange(-n,n+1,s):
for z in xrange(-n,n+1,s):
self.add_block((x,y-2,z),GRASS,immediate=False)
self.add_block((x,y-3,z),STONE,immediate=False)
if x in (-n,n) or z in (-n,n):
for dy in xrange(-2,3):
self.add_block((x,y+dy,z),STONE,immediate=False)
o=n-15
for _ in xrange(120):
a=random.randint(-o,o)
b=random.randint(-o,o)
c=-1
h=random.randint(2,6)
s=random.randint(4,12)
d=1
t=random.choice([GRASS, SAND,STONE])
for y in xrange(c,c+h):
for x in xrange(a-s,a+s+1):
for z in xrange(b-s,b+s+1):
if(x-a) ** 2 + (z-b)**2>(s+1)**2:
continue
if(x-0)**2+(z-0)**2<5**2:
continue
self.add_block((x,y,z),t,immediate=False)
s-=d
def hit_test(self,position,vector,max_distance=8):
""" Line of sight search from current position. If a block is
intersected it is returned, along with the block previously in the line
of sight. If no block is found, return None, None.
Parameters
----------
position:tuple of len 3
The (x,y,z) position to check visibility from.
vector:tuple of len 29
The line of sight vector.
max_distance:int
How many blocks away to search for a hit.
"""
m=8
x,y,z=position
dx,dy,dz=vector
previous=None
for _ in xrange(max_distance*m):
key=normalize((x,y,z))
if key!=previous and key in self.world:
return key,previous
previous=key
x,y,z=x+dx/m,y+dy/m,z+dz/m
return None, None
def exposed(self,position):
""" Returns False is given `position` is surrounded on all 6 sides by
blocks, True otherwise.
"""
x,y,z = position
for dx,dy,dz in FACES:
if(x+dx,y+dy,z+dz) not in self.world:
return True
return False
def add_block(self,position,texture,immediate=True):
""" Add a block with the given `texture` and `position` to the world.
Parameters
----------
position:tuple of len 3
The (x,y,z) position of the block to add.
texture:list of len 3
The coordinates of the texture squares. Use `tex_coords()` to
generate.
immediate:bool
Whether or not to draw the block immediately.
"""
if position in self.world:
self.remove_block(position, immediate)
self.world[position] = texture
self.sectors.setdefault(sectorize(position),[]).append(position)
if immediate:
if self.exposed(position):
self.show_block(position)
self.check_neighbors(position)
def remove_block(self,position,immediate=True):
""" Remove the block at the given `position`.
Parameters
----------
position:tuple of len 3
The (x,y,z) position of the block to remove.
immediate:bool
Whether or not to immediately remove block from canvas.
"""
del self.world[position]
self.sectors[sectorize(position)].remove(position)
if immediate:
if position in self.shown:
self.hide_block(position)
self.check_neighbors(position)
def check_neighbors(self,position):
""" Check all blocks surrounding `position` and ensure their visual
state is current.This means hiding blocks that are not exposed and
ensuring that all exposed blocks are shown.Usually used after a block
is added or removed.
"""
x,y,z=position
for dx,dy,dz in FACES:
key=(x+dx,y+dy,z+dz)
if key not in self.world:
continue
if self.exposed(key):
if key not in self.shown:
self.show_block(key)
else:
if key in self.shown:
self.hide_block(key)
def show_block(self,position,immediate=True):
""" Show the block at the given `position`. This method assumes the
block has already been added with add_block()
Parameters
----------
position:tuple of len 3
The (x,y,z) position of the block to show.
immediate:bool
Whether or not to show the block immediately.
"""
texture=self.world[position]
self.shown[position]=texture
if immediate:
self._show_block(position,texture)
else:
self._enqueue(self._show_block,position,texture)
def _show_block(self,position,texture):
""" Private implementation of the `show_block()` method.
Parameters
----------
position:tuple of len 3
The(x,y,z)position of the block to show.
texture:list of len 3
The coordinates of the texture squares.Use `tex_coords()`to
generate.
"""
x,y,z=position
vertex_data=cube_vertices(x,y,z,0.5)
texture_data=list(texture)
self._shown[position]=self.batch.add(24,GL_QUADS,self.group,
('v3f/static',vertex_data),
('t2f/static',texture_data))
def hide_block(self, position, immediate=True):
""" Hide the block at the given `position`. Hiding does not remove the
block from the world.
Parameters
----------
position:tuple of len 3
The (x,y,z) position of the block to hide.
immediate:bool
Whether or not to immediately remove the block from the canvas.
"""
self.shown.pop(position)
if immediate:
self._hide_block(position)
else:
self._enqueue(self._hide_block,position)
def _hide_block(self,position):
""" Private implementation of the'hide_block()'method.
"""
self._shown.pop(position).delete()
def show_sector(self,sector):
""" Ensure all blocks in the given sector that should be shown are
drawn to the canvas.
"""
for position in self.sectors.get(sector,[]):
if position not in self.shown and self.exposed(position):
self.show_block(position,False)
def hide_sector(self,sector):
""" Ensure all blocks in the given sector that should be hidden are
removed from the canvas.
"""
for position in self.sectors.get(sector,[]):
if position in self.shown:
self.hide_block(position,False)
def change_sectors(self,before,after):
""" Move from sector `before` to sector `after`. A sector is a
contiguous x, y sub-region of world. Sectors are used to speed up
world rendering.
"""
before_set=set()
after_set=set()
pad=4
for dx in xrange(-pad,pad+1):
for dy in [0]:
for dz in xrange(-pad,pad+1):
if dx**2+dy**2+dz**2>(pad+1)**2:
continue
if before:
x,y,z=before
before_set.add((x+dx,y+dy,z+dz))
if after:
x,y,z=after
after_set.add((x+dx,y+dy,z+dz))
show=after_set-before_set
hide=before_set-after_set
for sector in show:
self.show_sector(sector)
for sector in hide:
self.hide_sector(sector)
def _enqueue(self,func,*args):
""" Add`func`to the internal queue.
"""
self.queue.append((func,args))
def _dequeue(self):
""" Pop the top function from the internal queue and call it.
"""
func,args=self.queue.popleft()
func(*args)
def process_queue(self):
""" Process the entire queue while taking periodic breaks. This allows
the game loop to run 喵oothly. The queue contains calls to
_show_block() and _hide_block() so this method should be called if
add_block() or remove_block() was called with immediate=False
"""
start=time.time
while self.queue and time.time <1/ TICKS_PER_SEC:
self._dequeue()
def process_entire_queue(self):
""" Process the entire queue with no breaks.
"""
while self.queue:
self._dequeue()
class Window(pyglet.window.Window):
def __init__(self,*args,**kwargs):
super(Window,self).__init__(*args,**kwargs)
self.exclusive=False
self.flying=False
self.strafe=[0,0]
self.position=(0,0,0)
self.rotation=(0,0)
self.sector=None
self.reticle=None
self.dy=0
self.inventory=[BRICK, GRASS, SAND,STONE,RED,PURPLE,GREEN,BLUE,BLACK,ORANGE]
self.block=self.inventory[0]
self.num_keys=[
key._1,key._2,key._3,key._4,key._5,
key._6,key._7,key._8,key._9,key._0,key.E,key.R]
self.model=Model()
self.label=pyglet.text.Label('',font_name='Arial',font_size=18,
x=10, y=self.height-10,anchor_x='left',anchor_y='top',
color=(0,0,0,255))
pyglet.clock.schedule_interval(self.update,1.0/TICKS_PER_SEC)
def set_exclusive_mouse(self,exclusive):
""" If`exclusive`is True, the game will capture the mouse, if False
the game will ignore the mouse.
"""
super(Window,self).set_exclusive_mouse(exclusive)
self.exclusive=exclusive
def get_sight_vector(self):
""" Returns the current line of sight vector indicating the direction
the player is looking.
"""
x,y=self.rotation
m=math.cos(math.radians(y))
dy=math.sin(math.radians(y))
dx=math.cos(math.radians(x-90))*m
dz=math.sin(math.radians(x-90))*m
return (dx,dy,dz)
def get_motion_vector(self):
""" Returns the current motion vector indicating the velocity of the
player.
Returns
-------
vector:tuple of len 3
Tuple containing the velocity in x,y, and z respectively.
"""
if any(self.strafe):
x,y=self.rotation
strafe=math.degrees(math.atan2(*self.strafe))
y_angle=math.radians(y)
x_angle=math.radians(x+strafe)
if self.flying:
m=math.cos(y_angle)
dy=math.sin(y_angle)
if self.strafe[1]:
dy=0.00
m=1
if self.strafe[0]>0:
dy*=-1
dx=math.cos(x_angle)*m
dz=math.sin(x_angle)*m
else:
dy=0.00
dx=math.cos(x_angle)
dz=math.sin(x_angle)
else:
dy=0.00
dx=0.00
dz=0.00
return (dx,dy,dz)
def update(self,dt):
""" This method is scheduled to be called repeatedly by the pyglet
clock.
Parameters
----------
dt:float
The change in time since the last call.
"""
self.model.process_queue()
sector=sectorize(self.position)
if sector!=self.sector:
self.model.change_sectors(self.sector, sector)
if self.sector is None:
self.model.process_entire_queue()
self.sector=sector
m=8
dt=min(dt,0.2)
for _ in xrange(m):
self._update(dt/m)
def _update(self,dt):
""" Private implementation of the `update()` method. This is where most
of the motion logic lives, along with gravity and collision detection.
Parameters
----------
dt:float
The change in time since the last call.
"""
speed=FLYING_SPEED if self.flying else WALKING_SPEED
d=dt*speed
dx,dy,dz=self.get_motion_vector()
dx,dy,dz=dx*d,dy*d,dz*d
if not self.flying:
self.dy-=dt*GRAVITY
self.dy=max(self.dy,-TERMINAL_VELOCITY)
dy+=self.dy*dt
x,y,z=self.position
x,y,z=self.collide((x+dx,y+dy,z+dz),PLAYER_HEIGHT)
self.position=(x,y,z)
def collide(self,position,height):
""" Checks to see if the player at the given `position` and `height`
is colliding with any blocks in the world.
Parameters
----------
position:tuple of len 3
The(x,y,z)position to check for collisions at.
height:int or float
The height of the player.
Returns
-------
position:tuple of len 3
The new position of the player taking into account collisions.
"""
p=list(position)
np=normalize(position)
for face in FACES:
for i in xrange(3):
if not face[i]:
continue
d=(p[i]-np[i])*face[i]
if d>pad:
continue
for dy in xrange(height):
op=list(np)
op[1]-=dy
op[i]+=face[i]
if tuple(op) not in self.model.world:
continue
p[i]-=(d-pad)*face[i]
if face==(0,-1,0) or face==(0,1,0):
self.dy=0
break
return tuple(p)
def on_mouse_press(self,x,y,button,modifiers):
""" Called when a mouse button is pressed. See pyglet docs for button
amd modifier mappings.
Parameters
----------
x,y:int
The coordinates of the mouse click. Always center of the screen if
the mouse is captured.
button:int
Number representing mouse button that was clicked.1 = left button,
4=right button.
modifiers:int
Number representing any modifying keys that were pressed when the
mouse button was clicked.
"""
if self.exclusive:
vector=self.get_sight_vector()
block,previous=self.model.hit_test(self.position,vector)
if(button==mouse.RIGHT) or \
((button==mouse.LEFT)and(modifiers & key.MOD_CTRL)):
if previous:
self.model.add_block(previous,self.block)
elif button==pyglet.window.mouse.LEFT and block:
self.model.world[block]
self.model.remove_block(block)
else:
self.set_exclusive_mouse(True)
def on_mouse_motion(self,x,y,dx,dy):
""" Called when the player moves the mouse.
Parameters
----------
x,y:int
The coordinates of the mouse click.Always center of the screen if
the mouse is captured.
dx,dy:float
The movement of the mouse.
"""
if self.exclusive:
m=0.15
x,y=self.rotation
x,y=x+dx*m,y+dy*m
y=max(-90,min(90,y))
self.rotation=(x,y)
def on_key_press(self,symbol,modifiers):
""" Called when the player presses a key.See pyglet docs for key
mappings.
Parameters
----------
symbol:int
Number representing the key that was pressed.
modifiers:int
Number representing any modifying keys that were pressed.
"""
if symbol==key.W:
self.strafe[0]-=1
elif symbol==key.S:
self.strafe[0]+=1
elif symbol==key.A:
self.strafe[1]-=1
elif symbol==key.D:
self.strafe[1]+=1
elif symbol==key.SPACE:
if self.dy==0:
self.dy=JUMP_SPEED
elif symbol==key.ESCAPE:
self.set_exclusive_mouse(False)
elif symbol==key.TAB:
self.flying=not self.flying
elif symbol in self.num_keys:
index=(symbol-self.num_keys[0])%len(self.inventory)
self.block=self.inventory[index]
def on_key_release(self,symbol,modifiers):
""" Called when the player releases a key.See pyglet docs for key
mappings.
Parameters
----------
symbol:int
Number representing the key that was pressed.
modifiers:int
Number representing any modifying keys that were pressed.
"""
if symbol==key.W:
self.strafe[0]+=1
elif symbol==key.S:
self.strafe[0]-=1
elif symbol==key.A:
self.strafe[1]+=1
elif symbol==key.D:
self.strafe[1]-=1
def on_resize(self,width,height):
self.label.y=height-10
if self.reticle:
self.reticle.delete()
x,y=self.width//2,self.height//2
n=10
self.reticle=pyglet.graphics.vertex_list(4,
('v2i',(x-n,y,x+n,y,x,y-n,x,y+n))
)
def set_2d(self):
""" Configure OpenGL to draw in 2d.
"""
width,height=self.get_size()
glDisable(GL_DEPTH_TEST)
viewport=self.get_viewport_size()
glViewport(0,0,max(1,viewport[0]),max(1,viewport[1]))
glMatrixMode(GL_PROJECTION)
glLoadIdentity()
glOrtho(0,max(1,width),0,max(1,height),-1,1)
glMatrixMode(GL_MODELVIEW)
glLoadIdentity()
def set_3d(self):
""" Configure OpenGL to draw in 3d.
"""
width,height=self.get_size()
glEnable(GL_DEPTH_TEST)
viewport=self.get_viewport_size()
glViewport(0,0,max(1,viewport[0]),max(1,viewport[1]))
glMatrixMode(GL_PROJECTION)
glLoadIdentity()
gluPerspective(65.0, width / float(height),0.1,60.0)
glMatrixMode(GL_MODELVIEW)
glLoadIdentity()
x,y=self.rotation
glRotatef(x,0,1,0)
glRotatef(-y,math.cos(math.radians(x)),0,math.sin(math.radians(x)))
x,y,z=self.position
glTranslatef(-x,-y,-z)
def on_draw(self):
""" Called by pyglet to draw the canvas.
"""
self.clear()
self.set_3d()
glColor3d(1,1,1)
self.model.batch.draw()
self.draw_focused_block()
self.set_2d()
self.draw_label()
self.draw_reticle()
def draw_focused_block(self):
""" Draw black edges around the block that is currently under the
crosshairs.
"""
vector=self.get_sight_vector()
block=self.model.hit_test(self.position, vector)[0]
if block:
x,y,z=block
vertex_data=cube_vertices(x,y,z,0.51)
glColor3d(0,0,0)
glPolygonMode(GL_FRONT_AND_BACK,GL_LINE)
pyglet.graphics.draw(24, GL_QUADS,('v3f/static',vertex_data))
glPolygonMode(GL_FRONT_AND_BACK,GL_FILL)
def draw_label(self):
""" Draw the label in the top left of the screen.
"""
x,y,z=self.position
self.label.text='%02d(%.2f, %.2f, %.2f) %d / %d' % (
pyglet.clock.get_fps(),x,y,z,
len(self.model._shown),len(self.model.world))
self.label.draw()
def draw_reticle(self):
""" Draw the crosshairs in the center of the screen.
"""
glColor3d(0,0,0)
self.reticle.draw(GL_LINES)
def setup():
""" Basic OpenGL configuration.
"""
glClearColor(0.5,0.69,1.0,1)
glEnable(GL_CULL_FACE)
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST)
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST)
def main():
window=Window(width=990,height=500,caption='main',resizable=True)
window.set_exclusive_mouse(True)
setup()
pyglet.app.run()
if __name__=='__main__':
main()
【提示】
部分含有Python第三方库相关内容的作品,在海龟编辑器网页端无法运行哦!如遇到这种情况,可以打开下面的链接,下载海龟编辑器客户端:
https://python.codemao.cn