致敬永远的经典英雄无敌3, 本想在网上找个战棋游戏学习下,无奈没有发现python版本的,那就自己来写一个把。
游戏实现了类似英雄无敌3 中战斗场景的回合制玩法:
python 战棋游戏代码实现(2):六边形地图寻路和显示
生物行走选择截图如下:
图1
图1 中的步兵行走范围属性值是4,表示可以走4个方格,图中背景是深蓝色的方格就是该步兵可以走的方格,不可以直接到斜对角的方格。步兵头顶红色的是血条,当前轮到行动的生物背景方格颜色会变成浅蓝色。
游戏截图如下:
图2
图2中,目前轮到行动的生物是我方的左下角背景为浅蓝色的步兵,可以看到背景为深蓝色的方格为步兵可以行走的范围。背景为绿色的方格为目前选定要行走到得方格。鼠标指向敌方生物,如果敌方生物背景方格颜色变成黄色,表示可以攻击,可以看到允许攻击斜对角的敌人。图中还有石块,表示不能移动到得方格。
source\component\map.py 中的 map类保存地图信息,width 和 height 为地图的宽度和长度,bg_map 设置方格的背景颜色, entity_map 保存哪个方格上有生物。active_entity表示当前要行动的生物。
class Map():
def __init__(self, width, height, grid):
self.width = width
self.height = height
self.bg_map = [[0 for x in range(self.width)] for y in range(self.height)]
self.entity_map = [[None for x in range(self.width)] for y in range(self.height)]
self.active_entity = None
updateMap 函数,当我方生物选择行动时,设置生物可行走范围内的方格背景为 c.BG_RANGE 。active_entity 的 inRange 函数判断一个方格是否在行走范围内。
def updateMap(self):
for y in range(self.height):
for x in range(self.width):
self.bg_map[y][x] = c.BG_EMPTY
if self.entity_map[y][x] is not None and self.entity_map[y][x].isDead():
self.entity_map[y][x] = None
if self.active_entity is None or self.active_entity.state != c.IDLE:
return
map_x, map_y = self.active_entity.map_x, self.active_entity.map_y
self.bg_map[map_y][map_x] = c.BG_ACTIVE
for y in range(self.height):
for x in range(self.width):
if not self.isMovable(x,y):
continue
if self.active_entity.inRange(self, x, y):
self.bg_map[y][x] = c.BG_RANGE
mouse_x, mouse_y = pg.mouse.get_pos()
self.checkMouseMove(mouse_x, mouse_y)
drawBackground 函数根据 bg_map 中每个方格的类型 绘制方格的背景颜色, 然后绘制方格之前的黑色分隔线。
def drawBackground(self, surface):
pg.draw.rect(surface, c.LIGHTYELLOW, pg.Rect(0, 0, c.MAP_WIDTH, c.MAP_HEIGHT))
for y in range(self.height):
for x in range(self.width):
if self.bg_map[y][x] == c.BG_EMPTY:
color = c.LIGHTYELLOW
elif self.bg_map[y][x] == c.BG_ACTIVE:
color = c.SKY_BLUE
elif self.bg_map[y][x] == c.BG_RANGE:
color = c.NAVYBLUE
elif self.bg_map[y][x] == c.BG_SELECT:
color = c.GREEN
elif self.bg_map[y][x] == c.BG_ATTACK:
color = c.GOLD
pg.draw.rect(surface, color, (x * c.REC_SIZE, y * c.REC_SIZE,
c.REC_SIZE, c.REC_SIZE))
surface.blit(self.map_image, self.rect)
for y in range(self.height):
# draw a horizontal line
start_pos = (0, 0 + c.REC_SIZE * y)
end_pos = (c.MAP_WIDTH, c.REC_SIZE * y)
pg.draw.line(surface, c.BLACK, start_pos, end_pos, 1)
for x in range(self.width):
# draw a horizontal line
start_pos = (c.REC_SIZE * x, 0)
end_pos = (c.REC_SIZE * x, c.MAP_HEIGHT)
pg.draw.line(surface, c.BLACK, start_pos, end_pos, 1)
source\component\entity.py 中的entity类保存生物的状态和属性。group表示 生物属于敌我哪一方, map_x 和 map_y 表示初始时生物在地图上的位置。sheet是生物的图像,EntityAttr 类保存生物的所有属性。
class Entity():
def __init__(self, group, sheet, map_x, map_y, data):
self.group = group
self.group_id = group.group_id
self.map_x = map_x
self.map_y = map_y
self.frames = []
self.frame_index = 0
self.loadFrames(sheet)
self.image = self.frames[self.frame_index]
self.rect = self.image.get_rect()
self.rect.x = self.map_x * c.REC_SIZE + 5
self.rect.y = self.map_y * c.REC_SIZE + 8
self.attr = EntityAttr(data)
self.health = self.attr.max_health
self.weapon = None
self.enemy = None
self.state = c.IDLE
self.animate_timer = 0.0
self.current_time = 0.0
self.move_speed = c.MOVE_SPEED
从图2 可以看到如果在生物的行走范围中有其他生物或石块时,行走范围就不能直接判断出来。
Entity类中 inRange函数使用A寻路算法计算出到某一个方格的距离,判断是否在行走范围内。
A 算法的实现说明可以看我之前的一篇文章 A*算法实现
def inRange(self, map, map_x, map_y):
location = AStarSearch.AStarSearch(map, (self.map_x, self.map_y), (map_x, map_y))
if location is not None:
_, _, distance = AStarSearch.getFirstStepAndDistance(location)
if distance <= self.attr.range:
return True
return False
A*算法可以获取从起始位置到目标位置的一条最短路径,所以人物行走就是沿着这个路径从起始位置移动到目标位置。
walkToDestination 函数中 self.rect.x 和 self.rect.y 表示生物当前的坐标位置,self.next_x 和self.next_y 表示路径中下一个方格的坐标位置。
def walkToDestination(self, map):
if self.rect.x == self.next_x and self.rect.y == self.next_y:
source = (self.rect.x//c.REC_SIZE, self.rect.y//c.REC_SIZE)
dest = (self.dest_x//c.REC_SIZE, self.dest_y//c.REC_SIZE)
location = AStarSearch.AStarSearch(map, source, dest)
if location is not None:
map_x, map_y, _ = AStarSearch.getFirstStepAndDistance(location)
self.next_x = map_x * c.REC_SIZE + 5
self.next_y = map_y * c.REC_SIZE + 8
else:
self.state = c.IDLE
print('Error no path to walk to dest(%d,%d)' % (self.next_x, self.next_y))
if self.rect.x != self.next_x:
self.rect.x += self.move_speed if self.rect.x < self.next_x else -self.move_speed
elif self.rect.y != self.next_y:
self.rect.y += self.move_speed if self.rect.y < self.next_y else -self.move_speed
生物目前有3个状态:
def update(self, game_info, map):
self.current_time = game_info[c.CURRENT_TIME]
if self.state == c.WALK:
if (self.current_time - self.animate_timer) > 250:
if self.frame_index == 0:
self.frame_index = 1
else:
self.frame_index = 0
self.animate_timer = self.current_time
if self.rect.x != self.dest_x or self.rect.y != self.dest_y:
self.walkToDestination(map)
else:
map.setEntity(self.map_x, self.map_y, None)
self.map_x = self.dest_x // c.REC_SIZE
self.map_y = self.dest_y // c.REC_SIZE
map.setEntity(self.map_x, self.map_y, self)
if self.enemy is None:
self.state = c.IDLE
else:
self.state = c.ATTACK
elif self.state == c.ATTACK:
if self.attr.remote:
if self.weapon is None:
self.shoot(self.enemy)
else:
self.weapon.update()
if self.weapon.done:
self.weapon = None
self.enemy = None
self.state = c.IDLE
else:
self.putHurt(self.enemy)
self.enemy = None
self.state = c.IDLE
if self.state == c.IDLE:
self.frame_index = 0
游戏实现代码的github链接 战棋游戏
这边是csdn的下载链接 战棋游戏
1.编译环境
python3.7 + pygame1.9
2.运行
直接运行根目录下的 main.py
$ python main.py