练手项目,针对pygame的人物视角跟随

灵活运用矢量运算 pygame.math.Vector2

from itertools import cycle
import numpy as np
from matplotlib  import pyplot as plt
import cv2
import pygame

pygame.init()

import cv2
import numpy as np
import pygame

def gen_world_core():
    return np.random.randint(0, 2, (50, 50))

def gen_world_map(world_core):
    """
    生成world_map,遍历每个core的点,为0就画一个20*20的白色圆角矩形(圆角为4),为1就画一个20*20的绿色圆角矩形(圆角为4),
    然后转换为OpenCV格式消除绿色块内小白点,再返回pygame的surface
    """
    # 创建numpy数组用于OpenCV操作
    height, width = world_core.shape
    world_map_array = np.full((height * 20, width * 20, 3), [255, 255, 255], dtype=np.uint8)  # 白色背景
    
    # 遍历world_core的每个点,绘制圆角矩形
    for i in range(height):
        for j in range(width):
            color = (0, 255, 0) if world_core[i, j] == 1 else (255, 255, 255)  # 绿色或白色
            # 转换为BGR格式(OpenCV格式)
            bgr_color = (color[2], color[1], color[0])
            
            # 计算矩形的位置和尺寸
            rect_x, rect_y, rect_w, rect_h = j*20, i*20, 20, 20
            corner_radius = 5  # 圆角半径设为4
            
            # 使用OpenCV绘制圆角矩形
            # 首先绘制主体矩形(中间部分)
            top_left = (rect_x + corner_radius, rect_y)
            bottom_right = (rect_x + rect_w - corner_radius, rect_y + rect_h)
            cv2.rectangle(world_map_array, top_left, bottom_right, bgr_color, -1)
            
            # 然后绘制垂直部分(左右两侧)
            top_left = (rect_x, rect_y + corner_radius)
            bottom_right = (rect_x + rect_w, rect_y + rect_h - corner_radius)
            cv2.rectangle(world_map_array, top_left, bottom_right, bgr_color, -1)
            
            # 最后绘制四个圆角
            cv2.circle(world_map_array, (rect_x + corner_radius, rect_y + corner_radius), corner_radius, bgr_color, -1)
            cv2.circle(world_map_array, (rect_x + rect_w - corner_radius, rect_y + corner_radius), corner_radius, bgr_color, -1)
            cv2.circle(world_map_array, (rect_x + corner_radius, rect_y + rect_h - corner_radius), corner_radius, bgr_color, -1)
            cv2.circle(world_map_array, (rect_x + rect_w - corner_radius, rect_y + rect_h - corner_radius), corner_radius, bgr_color, -1)
    
    # 消除绿色块内部的白点
    cleaned_array = remove_white_spots_in_green_areas(world_map_array)
    
    # 将numpy数组转换为pygame surface
    world_map = pygame.surfarray.make_surface(cleaned_array.swapaxes(0, 1))
    
    return world_map

def remove_white_spots_in_green_areas(surface_array):
    """
    消除surface_array绿色块内部的白点(强力版)
    """
    # 复制输入数组
    output_array = surface_array.copy()
    
    # 将RGB转换为HSV色彩空间,更好地分离绿色
    hsv = cv2.cvtColor(surface_array, cv2.COLOR_RGB2HSV)
    
    # 定义绿色的HSV范围
    lower_green = np.array([35, 40, 40])
    upper_green = np.array([85, 255, 255])
    
    # 创建绿色掩码
    green_mask = cv2.inRange(hsv, lower_green, upper_green)
    
    # 使用较大核进行闭运算,填补较大的孔洞
    kernel_large = cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (4, 4))
    closed_mask = cv2.morphologyEx(green_mask, cv2.MORPH_CLOSE, kernel_large)
    
    # 识别原始绿色区域和闭运算后的区域差异
    diff_mask = cv2.bitwise_xor(green_mask, closed_mask)
    
    # 将闭运算后新增的区域(可能是填补的白点)转换为绿色
    output_array[diff_mask > 0] = [0, 255, 0]
    
    # 现在再次创建绿色掩码(包含已填充的区域)
    updated_hsv = cv2.cvtColor(output_array, cv2.COLOR_RGB2HSV)
    updated_green_mask = cv2.inRange(updated_hsv, lower_green, upper_green)
    
    # 创建白色像素掩码
    white_mask = np.all(output_array == [255, 255, 255], axis=-1)
    
    # 使用连通组件分析找出所有白色区域
    num_labels, labels, stats, centroids = cv2.connectedComponentsWithStats(white_mask.astype(np.uint8), connectivity=8)
    
    # 遍历所有连通组件(跳过背景组件0)
    for label in range(1, num_labels):
        # 获取当前组件的面积
        area = stats[label, cv2.CC_STAT_AREA]
        
        # 对于小面积白色区域(<= 8像素),检查它是否被绿色包围
        if area <= 8:
            # 获取该组件的坐标
            component_coords = np.where(labels == label)
            
            # 检查这个白色区域周围是否主要是绿色
            green_neighbors_count = 0
            total_neighbors_count = 0
            
            for y, x in zip(component_coords[0], component_coords[1]):
                # 检查周围一圈的像素
                for dy in [-1, 0, 1]:
                    for dx in [-1, 0, 1]:
                        ny, nx = y + dy, x + dx
                        
                        # 确保坐标在范围内
                        if 0 <= ny < output_array.shape[0] and 0 <= nx < output_array.shape[1]:
                            # 跳过组件本身的像素
                            if not (ny in component_coords[0] and nx in component_coords[1] and 
                                   np.where((component_coords[0] == ny) & (component_coords[1] == nx))[0].size > 0):
                                total_neighbors_count += 1
                                
                                # 检查是否为绿色
                                pixel = output_array[ny, nx]
                                if (lower_green[0] <= updated_hsv[ny, nx, 0] <= upper_green[0] and
                                    lower_green[1] <= updated_hsv[ny, nx, 1] <= upper_green[1] and
                                    lower_green[2] <= updated_hsv[ny, nx, 2] <= upper_green[2]):
                                    green_neighbors_count += 1
            
            # 如果超过一半的邻居是绿色,则将这个白色区域填成绿色
            if total_neighbors_count > 0 and green_neighbors_count / total_neighbors_count > 0.5:
                for y, x in zip(component_coords[0], component_coords[1]):
                    output_array[y, x] = [0, 255, 0]
    
    return output_array

def split_image_to_surfaces(image_path, rows=4, cols=4):
    """将大图切分为小Surface块"""
    original_surface = pygame.image.load(image_path).convert_alpha()
    width, height = original_surface.get_size()
    cell_width = width // cols
    cell_height = height // rows
    surface_list = []

    for row in range(rows):
        for col in range(cols):
            x = col * cell_width
            y = row * cell_height
            sub_surface = pygame.Surface((cell_width, cell_height), pygame.SRCALPHA).convert_alpha()
            sub_surface.blit(original_surface, (0, 0), (x, y, cell_width, cell_height))
            surface_list.append(sub_surface)

    return surface_list


class Girl(pygame.sprite.Sprite):
    def __init__(self, x=0, y=0, fps=60):
        super().__init__()
        self.state = "down"
        self.images = split_image_to_surfaces("/home/superexpo/图片/4299975-d035b678055553f2.png")
        self.image = self.images[0]
        self.world_rect = self.image.get_rect(topleft=(x, y))
        self.rect = self.world_rect.copy()
        
        frame_multiplier = int(60 / fps * 6)
        self.generators = {
            'down': cycle([item for item in self.images[0:4] for _ in range(frame_multiplier)]),
            'left': cycle([item for item in self.images[4:8] for _ in range(frame_multiplier)]),
            'right': cycle([item for item in self.images[8:12] for _ in range(frame_multiplier)]),
            'up': cycle([item for item in self.images[12:16] for _ in range(frame_multiplier)])
        }
        
        self.speed = pygame.math.Vector2(0, 0)
        self.acc = pygame.math.Vector2(0, 0)

    def update(self, world_map_rect, view):
        # 移动角色
        self.world_rect.move_ip(self.speed.x, self.speed.y)
        
        # 更新屏幕上的位置
        self.rect.topleft = view.calculate_sprite_pos(self)
        
        # 限制在世界地图边界内
        self.rect.clamp_ip(world_map_rect)
        self.world_rect.clamp_ip(world_map_rect)
        
        # 根据速度更新状态
        self._update_state()

    def _update_state(self):
        """根据速度向量更新角色状态(朝向)"""
        if abs(self.speed.x) > abs(self.speed.y):  # 水平移动占主导
            self.state = "left" if self.speed.x < 0 else "right"
        elif abs(self.speed.y) > abs(self.speed.x):  # 垂直移动占主导
            self.state = "up" if self.speed.y < 0 else "down"
        else:  # 速度相等时优先考虑水平方向
            if self.speed.x != 0:
                self.state = "left" if self.speed.x < 0 else "right"
            elif self.speed.y != 0:
                self.state = "up" if self.speed.y < 0 else "down"

    def draw(self, screen):
        """绘制角色"""
        screen.blit(next(self.generators[self.state]), self.rect)

    def move_towards_mouse(self, mouse_pos):
        """朝鼠标位置移动"""
        direction = pygame.math.Vector2(mouse_pos) - pygame.math.Vector2(self.rect.center)
        if direction.length() > 0:  # 避免除以零
            direction = direction.normalize() * 3
            self.acc = direction
            self.speed += self.acc
        else:
            self.acc = pygame.math.Vector2(0, 0)


class View:
    def __init__(self, screen):
        self.screen = screen
        self.rect = screen.get_rect().copy()

    def calculate_sprite_pos(self, sprite):
        """计算精灵在屏幕上的位置"""
        sprite_screen_pos = pygame.math.Vector2(sprite.world_rect.topleft) - pygame.math.Vector2(self.rect.topleft)
        return sprite_screen_pos

    def update(self, sprite, world_map):
        """更新视图位置,跟随精灵"""
        # 将视图中心设置为精灵的世界坐标
        self.rect.center = sprite.world_rect.center
        # 限制视图在世界地图边界内
        self.rect.clamp_ip(world_map.get_rect())


def handle_input(keys, girl):
    """处理键盘输入"""
    new_speed = pygame.math.Vector2(0, 0)
    
    if keys[pygame.K_RIGHT]:
        new_speed.x = 2
    elif keys[pygame.K_LEFT]:
        new_speed.x = -2
        
    if keys[pygame.K_UP]:
        new_speed.y = -2
    elif keys[pygame.K_DOWN]:
        new_speed.y = 2
        
    girl.speed = new_speed


def main():
    # 初始化游戏对象
    world_core = gen_world_core()
    world_map = gen_world_map(world_core)
    screen = pygame.display.set_mode((800, 600))
    clock = pygame.time.Clock()
    fps = 60
    
    girl = Girl(200, 300)
    view = View(screen)
    
    running = True
    while running:
        keys = pygame.key.get_pressed()
        handle_input(keys, girl)
        
        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                running = False
            elif event.type == pygame.MOUSEMOTION:
                girl.move_towards_mouse(event.pos)

        # 更新游戏状态
        girl.update(world_map.get_rect(), view)
        view.update(girl, world_map)

        # 渲染
        screen.fill(pygame.color.Color('white'))
        screen.blit(world_map, (-view.rect.x, -view.rect.y))
        girl.draw(screen)
        pygame.display.update()
        clock.tick(fps)

    pygame.quit()


if __name__ == "__main__":
    main()
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

友情链接更多精彩内容