一、运行效果
操作说明:
方向键控制地图移动
w键保存地图
r键读取地图
鼠标左键设置障碍
鼠标右键删除障碍
二、前言
之前呢,写过一个python的A星算法类(A星算法),运行结果只能在控制台查看,太不爽了。A星算法可是游戏里常用的寻路算法,当然是要结合游戏使用才过瘾。
所以,我打算写几篇关于2drpg行走系统开发的文章。
写行走系统之前,我们得有个地图编辑器。将地图行走层编辑保存为文件,然后在游戏里读取这个行走层文件。
三、开发思路
游戏中的地图其实就是一个二维数组,我们的地图编辑器只需要将这个二维数组相应位置的值改变就行了。我们可以人为规定map[x][y]==0的时候是可行走的,map[x][y]==1的时候是障碍。
所以,地图编辑器就是用来编辑这个二维数组,并且将它可视化。
在我写的这个简易的地图编辑器中,规定16*16个像素为一个格子,当然你完全可以改成其他尺寸。
四、核心算法
鼠标点下去的位置是如何与二维数组下标对应的呢?这就是本文的重点。
在解决一个问题之前,我们得理一理已知条件是什么,需要求什么。
已知:
map_x,map_y 地图绘图坐标,也就是地图图片的左上角相对于窗口的坐标。
mouse_x,mouse_y 鼠标在窗口中的坐标
求:
indexX,indexY 鼠标点击位置所对应的地图二维数组中的下标
如图所示:
绿色的是游戏地图,黑色的是游戏窗口,红色点是鼠标点击的位置。
那么,鼠标相对于地图的坐标是多少呢?
设mouse_map_x,mouse_map_y为鼠标相对于地图的坐标,如图:
显然mouse_map_x=mouse_x+(-map_x),但是为什么是加(-map_x)呢?因为map_x是相对于游戏窗口的坐标,所以它总是一个负数,所以加一个负号让它变成正数。
同理mouse_map_y=mouse_y+(-map_y)
那么我们所求的indexX,indexY就很容易计算出来了,上面说过了一个格子占16*16个像素。
所以:
indexX=int(mouse_map_x/16)
indexY=int(mouse_map_y/16)
ok啦,下面放上代码(源码里的命名和上述命名有所不同,还请大家注意啦)。
五、源码
"""
为演示A星算法的实际用途,而专门开发的地图编辑器
"""
import pygame
class Array2D:
"""
说明:
1.构造方法需要两个参数,即二维数组的宽和高
2.成员变量w和h是二维数组的宽和高
3.使用:‘对象[x][y]’可以直接取到相应的值
4.数组的默认值都是0
"""
def __init__(self, w, h):
self.w = w
self.h = h
self.data = [[0 for y in range(h)] for x in range(w)]
def showArray2D(self):
for y in range(self.h):
for x in range(self.w):
print(self.data[x][y], end=' ')
print("")
def __getitem__(self, item):
return self.data[item]
winSur = None # 窗口的surface
map2d = None # 地图的二维数组
mapImg = None # 地图的图片
x, y = [0, 0] # 地图当前绘图坐标
dirKeyState = [0, 0, 0, 0] # 下,上,右,左方向键状态,0没按下 1按下
def init():
global winSur, map2d, mapImg
pygame.init()
pygame.display.set_caption("地图行走层编辑器")
winSur = pygame.display.set_mode((640, 480))
mapImg = pygame.image.load('./images/btm_1_0.jpg')
# 以16*16像素为一个可行走的格子
map2d = Array2D(int(mapImg.get_width() / 16), int(mapImg.get_height() / 16))
def writeMap():
with open('./map01.map', mode='w', encoding='utf8') as file:
file.write(str(map2d.data))
print("保存地图成功!")
def readMap():
global map2d
with open('./map01.map', mode='r', encoding='utf8') as file:
data = file.read()
map2d.data = eval(data)
map2d.w = len(map2d.data)
map2d.h = len(map2d.data[0])
print("读取地图成功!")
def moveMap():
"""
移动地图
:return:
"""
global x, y
step = 8
if dirKeyState[0] == 1:
y += step
if y + step > 0:
y = 0
if dirKeyState[1] == 1:
y -= step
if y - step < -(mapImg.get_height() - 480):
y = -(mapImg.get_height() - 480)
if dirKeyState[2] == 1:
x += step
if x + step > 0:
x = 0
if dirKeyState[3] == 1:
x -= step
if x - step < -(mapImg.get_width() - 640):
x = -(mapImg.get_width() - 640)
def drawMap():
"""
绘制不可行走区域
:return:
"""
for ty in range(map2d.h):
for tx in range(map2d.w):
if map2d[tx][ty] == 1:
pygame.draw.rect(winSur, (255, 255, 255), (x + tx * 16 + 1, y + ty * 16 + 1, 14, 14), 1)
def mainLoop():
global dirKeyState, map2d
# 相关参数
while True:
for event in pygame.event.get():
if event.type == pygame.QUIT:
exit()
elif event.type == pygame.KEYDOWN:
if event.key == pygame.K_UP:
dirKeyState[0] = 1
elif event.key == pygame.K_DOWN:
dirKeyState[1] = 1
elif event.key == pygame.K_LEFT:
dirKeyState[2] = 1
elif event.key == pygame.K_RIGHT:
dirKeyState[3] = 1
elif event.key == pygame.K_r:
readMap() # 读取地图
elif event.key == pygame.K_w:
writeMap() # 保存地图
elif event.type == pygame.KEYUP:
if event.key == pygame.K_UP:
dirKeyState[0] = 0
elif event.key == pygame.K_DOWN:
dirKeyState[1] = 0
elif event.key == pygame.K_LEFT:
dirKeyState[2] = 0
elif event.key == pygame.K_RIGHT:
dirKeyState[3] = 0
elif event.type == pygame.MOUSEBUTTONDOWN:
mouse_x, mouse_y = pygame.mouse.get_pos() # 获得当前鼠标坐标
map_x = mouse_x + (-x)
map_y = mouse_y + (-y)
cell_x = int(map_x / 16)
cell_y = int(map_y / 16)
if pygame.mouse.get_pressed() == (1, 0, 0): # 鼠标左键按下
map2d[cell_x][cell_y] = 1
if pygame.mouse.get_pressed() == (0, 0, 1): # 鼠标左键按下
map2d[cell_x][cell_y] = 0
pygame.time.delay(32)
# 逻辑更新
moveMap()
# 绘图更新
winSur.blit(mapImg, (x, y))
drawMap()
pygame.display.flip()
if __name__ == '__main__':
init()
mainLoop()