简略的战旗游戏开发学习

在网上找寻教程之后搞出了这么个雏形

游戏介绍

游戏实现了战斗场景的回合制玩法: 对战单方每个生物每一轮有一次口头机会,能够行走或攻打对方。 每个生物属性有:行走范畴,速度,生命,挫伤,进攻,攻打 和 是否是近程兵种。 当把对方生物都毁灭时,即胜利。

代码介绍

对于战旗类游戏的外围还是地图,尽管网上有六边形的地图教程然而没看太懂就做正方形的吧 首先定义函数,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        self.select = None        self.setupMapImage(grid)        self.setupMouseImage()

当我方生物抉择口头时,设置生物可行走范畴内的方格背景为 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) or not self.isValid(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)

接下来欠缺下咱们的map代码

import pygame as pgfrom .. import toolfrom .. import constants as cclass 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        self.select = None        self.setupMapImage(grid)        self.setupMouseImage()    def setupMapImage(self, grid):        self.grid_map = [[0 for x in range(self.width)] for y in range(self.height)]        if grid is not None:            for data in grid:                x, y, type = data['x'], data['y'], data['type']                self.grid_map[y][x] = type                self.map_image = pg.Surface((self.width * c.REC_SIZE, self.height * c.REC_SIZE)).convert()        self.rect = self.map_image.get_rect()        self.rect.x = 0        self.rect.y = 0        for y in range(self.height):            for x in range(self.width):                type = self.grid_map[y][x]                if type != c.MAP_EMPTY:                    if c.MAP_HEXAGON:                        base_x, base_y = tool.getHexMapPos(x, y)                        self.map_image.blit(tool.GRID[type], (base_x, base_y))                    else:                        self.map_image.blit(tool.GRID[type], (x * c.REC_SIZE, y * c.REC_SIZE))        self.map_image.set_colorkey(c.BLACK)    def setupMouseImage(self):        self.mouse_frames = []        frame_rect = (0, 0, 25, 27)        self.mouse_image = tool.get_image(tool.GFX[c.MOUSE], *frame_rect, c.BLACK, 1)        self.mouse_rect = self.mouse_image.get_rect()        pg.mouse.set_visible(False)    def isValid(self, map_x, map_y):        if c.MAP_HEXAGON:            if map_y % 2 == 0:                max_x = self.width            else:                max_x = self.width - 1        else:            max_x = self.width        if (map_x < 0 or map_x >= max_x or            map_y < 0 or map_y >= self.height):            return False        return True        def isMovable(self, map_x, map_y):        return (self.entity_map[map_y][map_x] == None and                 self.grid_map[map_y][map_x] != c.MAP_STONE)    def getMapIndex(self, x, y):        if c.MAP_HEXAGON:            return tool.getHexMapIndex(x, y)        else:            return (x//c.REC_SIZE, y//c.REC_SIZE)    def getDistance(self, x1, y1, map_x2, map_y2):        if c.MAP_HEXAGON:            x2, y2 = tool.getHexMapPos(map_x2, map_y2)            x2 += c.HEX_X_SIZE // 2            y2 += c.HEX_Y_SIZE // 2            distance = (abs(x1 - x2) + abs(y1 - y2))        else:            map_x1, map_y1 = self.getMapIndex(x1, y1)            x2 = map_x2 * c.REC_SIZE + c.REC_SIZE//2            y2 = map_y2 * c.REC_SIZE + c.REC_SIZE//2            distance = (abs(x1 - x2) + abs(y1 - y2))            if map_x1 != map_x2 and map_y1 != map_y2:               distance -= c.REC_SIZE//2        return distance    def checkMouseClick(self, x, y):        if self.active_entity is None:            return False                map_x, map_y = self.getMapIndex(x, y)        if not self.isValid(map_x, map_y):            return False        entity = self.entity_map[map_y][map_x]        if ((entity is None or entity == self.active_entity) and             self.active_entity.inRange(self, map_x, map_y)):            self.active_entity.setDestination(map_x, map_y)            return True        elif entity is not None:            if self.active_entity.isRemote():                self.active_entity.setTarget(entity)                return True            elif self.select is not None:                self.active_entity.setDestination(self.select[0], self.select[1], entity)                return True        return False    def checkMouseMove(self, x, y):        if self.active_entity is None:            return False        map_x, map_y = self.getMapIndex(x, y)        if not self.isValid(map_x, map_y):            return False                self.select = None        entity = self.entity_map[map_y][map_x]        if ((self.isMovable(map_x, map_y) or entity == self.active_entity) and             self.active_entity.inRange(self, map_x, map_y)):                self.bg_map[map_y][map_x] = c.BG_SELECT        elif entity is not None:            if entity.group_id != self.active_entity.group_id:                if self.active_entity.isRemote():                    self.bg_map[map_y][map_x] = c.BG_ATTACK                else:                    dir_list = tool.getAttackPositions(map_x, map_y)                    res_list = []                    for offset_x, offset_y in dir_list:                        if self.isValid(map_x + offset_x, map_y + offset_y):                            type = self.bg_map[map_y + offset_y][map_x + offset_x]                            if type == c.BG_RANGE or type == c.BG_ACTIVE:                                res_list.append((map_x + offset_x, map_y + offset_y))                    if len(res_list) > 0:                        min_dis = c.MAP_WIDTH                        for tmp_x, tmp_y in res_list:                            distance = self.getDistance(x, y, tmp_x, tmp_y)                            if distance < min_dis:                                min_dis = distance                                res = (tmp_x, tmp_y)                        self.bg_map[res[1]][res[0]] = c.BG_SELECT                        self.bg_map[map_y][map_x] = c.BG_ATTACK                        self.select = res    def setEntity(self, map_x, map_y, value):        self.entity_map[map_y][map_x] = value    def drawMouseShow(self, surface):        x, y = pg.mouse.get_pos()        map_x, map_y = self.getMapIndex(x, y)        if self.isValid(map_x, map_y):            self.mouse_rect.x = x            self.mouse_rect.y = y            surface.blit(self.mouse_image, self.mouse_rect)    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) or not self.isValid(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)    def drawBackground(self, surface):        if c.MAP_HEXAGON:            return self.drawBackgroundHex(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)    def calHeuristicDistance(self, x1, y1, x2, y2):        if c.MAP_HEXAGON:            dis_y = abs(y1 - y2)            dis_x = abs(x1 - x2)            half_y = dis_y // 2            if dis_y >= dis_x:                dis_x = 0            else:                dis_x -= half_y            return (dis_y + dis_x)        else:            return abs(x1 - x2) + abs(y1 - y2)    def drawBackgroundHex(self, surface):        Y_LEN = c.HEX_Y_SIZE // 2        X_LEN = c.HEX_X_SIZE // 2        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                base_x, base_y = tool.getHexMapPos(x, y)                points = [(base_x, base_y + Y_LEN//2 + Y_LEN), (base_x, base_y + Y_LEN//2),                          (base_x + X_LEN, base_y), (base_x + X_LEN * 2, base_y + Y_LEN//2),                          (base_x + X_LEN * 2, base_y + Y_LEN//2 + Y_LEN), (base_x + X_LEN, base_y + Y_LEN*2)]                pg.draw.polygon(surface, color, points)        surface.blit(self.map_image, self.rect)        for y in range(self.height):            for x in range(self.width):                if y % 2 == 1 and x == self.width - 1:                    continue                base_x, base_y = tool.getHexMapPos(x, y)                points = [(base_x, base_y + Y_LEN//2 + Y_LEN), (base_x, base_y + Y_LEN//2),                          (base_x + X_LEN, base_y), (base_x + X_LEN * 2, base_y + Y_LEN//2),                          (base_x + X_LEN * 2, base_y + Y_LEN//2 + Y_LEN), (base_x + X_LEN, base_y + Y_LEN*2)]                pg.draw.lines(surface, c.BLACK, True, points)

接下来咱们要搞定的就是人物类,咱们的函数要实现:1.生物属于敌我哪一方 2.初始时生物在地图上的地位 3.保留生物的所有属性 4.生物的行走 5.生物的三种状态那么看代码

import pygame as pgfrom .. import toolfrom .. import constants as cfrom .. import AStarSearchfrom . import mapclass FireBall():    def __init__(self, x, y, enemy, hurt):        # first 3 Frames are flying, last 4 frams are exploding        frame_rect = (0,0,14,14)        self.image = tool.get_image(tool.GFX[c.FIREBALL], *frame_rect, c.BLACK, c.SIZE_MULTIPLIER)        self.rect = self.image.get_rect()        self.rect.centerx = x        self.rect.centery = y        self.enemy = enemy        self.hurt = hurt        self.done = False        self.calVelocity()        def calVelocity(self):        #print('calVelocity: x:', self.enemy.rect.centerx, self.rect.centerx, 'y:', self.enemy.rect.centery,self.rect.centery)        dis_x = self.enemy.rect.centerx - self.rect.centerx        dis_y = self.enemy.rect.centery - self.rect.centery        distance = (dis_x ** 2 + dis_y ** 2) ** 0.5        self.x_vel = (dis_x * 10)/distance        self.y_vel = (dis_y * 10)/distance            def update(self):        self.rect.x += self.x_vel        self.rect.y += self.y_vel        if abs(self.rect.x - self.enemy.rect.x) + abs(self.rect.y - self.enemy.rect.y) < 25:            self.enemy.setHurt(self.hurt)            self.done = True        def draw(self, surface):        surface.blit(self.image, self.rect)class EntityAttr():    def __init__(self, data):        self.max_health = data[c.ATTR_HEALTH]        self.range = data[c.ATTR_RANGE]        self.damage = data[c.ATTR_DAMAGE]        self.attack = data[c.ATTR_ATTACK]        self.defense = data[c.ATTR_DEFENSE]        self.speed = data[c.ATTR_SPEED]        if data[c.ATTR_REMOTE] == 0:            self.remote = False        else:            self.remote = True        def getHurt(self, enemy_attr):        offset = 0        if self.attack > enemy_attr.defense:            offset = (self.attack - enemy_attr.defense) * 0.05        elif self.attack < enemy_attr.defense:            offset = (self.attack - enemy_attr.defense) * 0.025        hurt = int(self.damage * (1 + offset))        return hurtclass 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.rect.y = self.getRectPos(map_x, map_y)                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        def getRectPos(self, map_x, map_y):        if c.MAP_HEXAGON:            base_x, base_y = tool.getHexMapPos(map_x, map_y)            return (base_x + 4, base_y + 6)        else:            return(map_x * c.REC_SIZE + 5, map_y * c.REC_SIZE + 8)    def getRecIndex(self, x, y):        if c.MAP_HEXAGON:            x += c.HEX_X_SIZE // 2 - 4            y += c.HEX_Y_SIZE // 2 - 6            map_x, map_y = tool.getHexMapIndex(x, y)        else:            map_x, map_y = (x//c.REC_SIZE, y//c.REC_SIZE)        return (map_x, map_y)    def loadFrames(self, sheet):        frame_rect_list = [(64, 0, 32, 32), (96, 0, 32, 32)]        for frame_rect in frame_rect_list:            self.frames.append(tool.get_image(sheet, *frame_rect,                             c.BLACK, c.SIZE_MULTIPLIER))            def setDestination(self, map_x, map_y, enemy=None):        self.dest_x, self.dest_y = self.getRectPos(map_x, map_y)        self.next_x, self.next_y = self.rect.x, self.rect.y        self.enemy = enemy        self.state = c.WALK        def setTarget(self, enemy):        self.enemy = enemy        self.state = c.ATTACK    def getHealthRatio(self):        if self.health > 0:            return self.health / self.attr.max_health        else:            return 0        def isDead(self):        return self.health <= 0        def isRemote(self):        return self.attr.remote    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        def putHurt(self, enemy):        hurt = self.attr.getHurt(enemy.attr)        enemy.setHurt(hurt)    def setHurt(self, damage):        self.health -= damage        if self.isDead():            self.group.removeEntity(self)        def shoot(self, enemy):        hurt = self.attr.getHurt(enemy.attr)        self.weapon = FireBall(*self.rect.center, self.enemy, hurt)         def walkToDestination(self, map):        if self.rect.x == self.next_x and self.rect.y == self.next_y:            source = self.getRecIndex(self.rect.x, self.rect.y)            dest = self.getRecIndex(self.dest_x, self.dest_y)            location = AStarSearch.AStarSearch(map, source, dest)            if location is not None:                map_x, map_y, _ = AStarSearch.getFirstStepAndDistance(location)                self.next_x, self.next_y = self.getRectPos(map_x, map_y)            else:                self.state = c.IDLE        if c.MAP_HEXAGON and self.rect.x != self.next_x and self.rect.y != self.next_y:            self.rect.x += self.move_speed if self.rect.x < self.next_x else -self.move_speed            self.rect.y += self.move_speed if self.rect.y < self.next_y else -self.move_speed        elif 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    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.map_y = self.getRecIndex(self.dest_x, self.dest_y)                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    def draw(self, surface):        self.image = self.frames[self.frame_index]        surface.blit(self.image, self.rect)        width = self.rect.width * self.getHealthRatio()        height = 5        pg.draw.rect(surface, c.RED, pg.Rect(self.rect.left, self.rect.top - height - 1, width, height))                if self.weapon is not None:            self.weapon.draw(surface)class EntityGroup():    def __init__(self, group_id):        self.group = []        self.group_id =  group_id        self.entity_index = 0    def createEntity(self, entity_list, map):        for data in entity_list:            entity_name, map_x, map_y = data['name'], data['x'], data['y']            if map_x < 0:                map_x = c.GRID_X_LEN + map_x            if map_y < 0:                map_y = c.GRID_Y_LEN + map_y                        entity = Entity(self, tool.GFX[entity_name], map_x, map_y, tool.ATTR[entity_name])            self.group.append(entity)            map.setEntity(map_x, map_y, entity)                #self.group = sorted(self.group, key=lambda x:x.attr.speed, reverse=True)        def removeEntity(self, entity):        for i in range(len(self.group)):            if self.group[i] == entity:                if (self.entity_index > i or                    (self.entity_index >= len(self.group) - 1)):                    self.entity_index -= 1        self.group.remove(entity)        def isEmpty(self):        if len(self.group) == 0:            return True        return False    def nextTurn(self):        self.entity_index = 0    def getActiveEntity(self):        if self.entity_index >= len(self.group):            entity = None        else:            entity = self.group[self.entity_index]        return entity    def consumeEntity(self):        self.entity_index += 1    def update(self, game_info, map):        for entity in self.group:            entity.update(game_info, map)    def draw(self, surface):        for entity in self.group:            entity.draw(surface)

实现这两个类后咱们的游戏(手游)基本上就实现了

游戏运行截图