Carla自动驾驶仿真四:pygame渲染Camera画面及车辆控制(代码详解)

news2024/11/22 9:58:37

文章目录

  • 前言
  • 一、依赖库安装
      • 1、pygame安装
      • 2、numpy安装
  • 二、Pygame渲染Carla Camera画面
      • 1、连接Carla并初始化TrafficManager
      • 2、生成自动驾驶车辆并设置交通行为
      • 3、创建初始化pygame surface对象的函数
      • 4、创建pygame处理Carla图像的回调函数
      • 5、创建pygame键盘控制车辆运动的函数
      • 6、创建相对于主车的Camera sensor
      • 7、创建Camera图像转pygame图像的回调函数
      • 8、运动控制初始化及画面渲染初始化
      • 9、更新pygame画面及处理车辆控制的键盘事件
      • 10、退出pygame结束仿真
  • 三、运行Carla和pygame
      • 1、打开Carla客户端
      • 2、运行完整代码
      • 3、运行效果展示


提示:以下是本篇文章正文内容,下面案例可供参考

前言

pygame提供了一种渲染实时视觉输出的方法,显示camera sensor的输出。我们也通过视频注入的方法将视频数据注入到控制器内部,提供视觉感知的场景,模拟真实场景进行仿真。

一、依赖库安装

1、pygame安装

- sudo apt-get install python3-pip
- sudo pip3 install pygame

2、numpy安装

- sudo pip3 install numpy

二、Pygame渲染Carla Camera画面

1、连接Carla并初始化TrafficManager

import carla
import random
import pygame
import numpy as np

#连接Carla客户端
client = carla.Client('localhost', 2000)
world = client.get_world()

# 设置Carla的仿真方式为同步模式
settings = world.get_settings()
settings.synchronous_mode = True # 开启同步模式
settings.fixed_delta_seconds = 0.05 #设置仿真的更新周期
world.apply_settings(settings) #应用设置

# 设置TrafficManager为同步模式,注意使用TraffiManager一定要设置Carla为同步模式
traffic_manager = client.get_trafficmanager()
traffic_manager.set_synchronous_mode(True) #应用同步模式

# 控制随机数生成器的输出,使得在相同种子下生成的随机数序列可重复
traffic_manager.set_random_device_seed(0)
random.seed(0)

2、生成自动驾驶车辆并设置交通行为

# 随机获得Carla地图上的一个坐标点
spawn_point = random.choice(world.get_map().get_spawn_points())

# 生成车辆并设置自动驾驶
vehicle_bp = random.choice(world.get_blueprint_library().filter('*vehicle*')) #随机选择车辆蓝图
ego_vehicle = world.spawn_actor(vehicle_bp, spawn_point) #在坐标点生成车辆
ego_vehicle.set_autopilot(True) #设置自动驾驶模式

#设置100%忽略交通灯
traffic_manager.ignore_lights_percentage(ego_vehicle, 100) 

3、创建初始化pygame surface对象的函数

  • 通过这段代码,我们可以得知RenderObject类的作用是创建一个具有指定宽度和高度的随机图像的渲染对象。这个对象可以在pygame中用于显示或其他图形操作。
# Render object to keep and pass the PyGame surface
class RenderObject(object):
    def __init__(self, width, height):
        """
            使用numpy生成一个随机的图像矩阵,大小为(height, width, 3)
            3表示RGB三个颜色通道
        """
        init_image = np.random.randint(0,255,(height,width,3),dtype='uint8')
        
   		"""
	        将图像矩阵转换为pygame的Surface对象
	        注意swapaxes函数的作用是交换矩阵的轴,将宽度和高度的顺序交换
	        这是因为pygame中Surface对象的宽度在前,高度在后
    	"""
        self.surface = pygame.surfarray.make_surface(init_image.swapaxes(0,1))

4、创建pygame处理Carla图像的回调函数

  • 这段代码的目的是将一个包含 RGBA 通道的图像数据转换为 RGB 格式的 pygame 可以使用的 Surface 对象,并将其赋值给 obj.surface。这个函数可能是用于在 pygame 中处理图像数据的回调函数或处理函数。
def pygame_callback(data, obj):
    # 将data.raw_data复制并重新形状为 (data.height, data.width, 4) 的图像矩阵
    img = np.reshape(np.copy(data.raw_data), (data.height, data.width, 4))
    
    # 从图像矩阵中移除alpha通道,只保留RGB三个颜色通道
    img = img[:,:,:3]
    
    # 反转颜色通道的顺序,由BGR转为RGB
    img = img[:, :, ::-1]
    
    # 将图像矩阵转换为pygame的Surface对象,并交换宽度和高度的顺序
    obj.surface = pygame.surfarray.make_surface(img.swapaxes(0,1))

5、创建pygame键盘控制车辆运动的函数

  • 这段代码的目的是为了通过结合pygame与键盘来控制车辆运动。
  • 键盘事件
    • 回车键:关闭自动驾驶行为 上键:前进 下键:后退 左键:往左 右键:往右
class ControlObject(object):
    def __init__(self, veh):

        # 车辆的初始控制状态
        self._vehicle = veh
        self._steer = 0
        self._throttle = False
        self._brake = False
        self._steer = None
        self._steer_cache = 0
		#获得carla控制
        self._control = carla.VehicleControl()

    #检查pygame按下的键盘事件
    def parse_control(self, event):
        if event.type == pygame.KEYDOWN:
            if event.key == pygame.K_RETURN:
                self._vehicle.set_autopilot(False)
            if event.key == pygame.K_UP:
                self._throttle = True
            if event.key == pygame.K_DOWN:
                self._brake = True
            if event.key == pygame.K_RIGHT:
                self._steer = 1
            if event.key == pygame.K_LEFT:
                self._steer = -1
        if event.type == pygame.KEYUP:
            if event.key == pygame.K_UP:
                self._throttle = False
            if event.key == pygame.K_DOWN:
                self._brake = False
                self._control.reverse = False
            if event.key == pygame.K_RIGHT:
                self._steer = None
            if event.key == pygame.K_LEFT:
                self._steer = None

    # 设置简单的动力控制函数
    def process_control(self):
		#上键:踩下油门踏板时,松开刹车踏板,油门最大的开度为0.5,可以自行设置。
        if self._throttle: 
            self._control.throttle = min(self._control.throttle + 0.01, 0.5)
            self._control.gear = 1
            self._control.brake = False
        elif not self._brake:
            self._control.throttle = 0.0

        if self._brake:
            # 下键后退:当车速小于0.01且不是倒档,设置倒档,并设置最大油门开度为0.5。
            if self._vehicle.get_velocity().length() < 0.01 and not self._control.reverse:
                self._control.brake = 0.0
                self._control.gear = 1
                self._control.reverse = True
                self._control.throttle = min(self._control.throttle + 0.1, 0.5)
            #下键后退:如果为倒档,则控制油门最大开度为0.5。
            elif self._control.reverse:
                self._control.throttle = min(self._control.throttle + 0.1, 0.5)
            #下键减速:设置减速踏板的最大开度为1。
            else:
                self._control.throttle = 0.0
                self._control.brake = min(self._control.brake + 0.3, 1)
        else:
            self._control.brake = 0.0
		
		#简单的左右方向控制
        if self._steer is not None:
            if self._steer == 1:
                self._steer_cache += 0.03
            if self._steer == -1:
                self._steer_cache -= 0.03
            min(0.7, max(-0.7, self._steer_cache))
            self._control.steer = round(self._steer_cache,1)
        else:
            if self._steer_cache > 0.0:
                self._steer_cache *= 0.2
            if self._steer_cache < 0.0:
                self._steer_cache *= 0.2
            if 0.01 > self._steer_cache > -0.01:
                self._steer_cache = 0.0
            self._control.steer = round(self._steer_cache,1)

        # 应用控制参数控制ego_vehicle
        self._vehicle.apply_control(self._control)

6、创建相对于主车的Camera sensor

  • 相对于ego_vehicle生成camera sensor
# 设置camera的初始坐标系
camera_init_trans = carla.Transform(carla.Location(x=-5, z=3), carla.Rotation(pitch=-20))
#获取camera sensor蓝图
camera_bp = world.get_blueprint_library().find('sensor.camera.rgb')
#相对于ego_vehicle生成camera sensor
camera = world.spawn_actor(camera_bp, camera_init_trans, attach_to=ego_vehicle)

7、创建Camera图像转pygame图像的回调函数

  • 通过这段代码,我们可以推断出该代码片段的目的是启动摄像头,并在每次摄像头有新图像时,调用 pygame_callback 函数对图像进行处理,并将结果更新到 renderObject 对象的 surface 属性中。这样可以实现实时更新渲染对象的显示。请确保在此之前已经定义了 pygame_callback 函数和 renderObject 对象。
"""
	在这里,camera 是一个摄像头对象,其具有 listen 方法。listen 方法接受一个回调函数作为参数,并在每次摄像头有新图像时调用该回调函数。
	
	在这个例子中,使用了一个匿名函数作为回调函数:lambda image: pygame_callback(image, renderObject)。这个回调函数接受一个 image 参数,然后调用 pygame_callback 函数,传递该图像和一个名为 renderObject 的对象作为参数。
"""
camera.listen(lambda image: pygame_callback(image, renderObject))

8、运动控制初始化及画面渲染初始化

# 设置画面的长宽
image_w = 800
image_h = 600

# 实例渲染对象以及车辆控制
renderObject = RenderObject(image_w, image_h)
controlObject = ControlObject(ego_vehicle)

# 初始化pygame
pygame.init()

"""
	在这里,pygame.display.set_mode 是 Pygame 中用于创建窗口的函数。它接受一个表示窗口宽度和高度的元组 (image_w, image_h) 作为第一个参数。
	
	第二个参数 pygame.HWSURFACE | pygame.DOUBLEBUF 是一个位掩码,用于指定窗口的模式选项。pygame.HWSURFACE 表示使用硬件加速的表面,pygame.DOUBLEBUF 表示使用双缓冲。
	
	使用硬件加速的表面 (pygame.HWSURFACE) 可以利用计算机的图形硬件来加速渲染,从而提高性能。双缓冲 (pygame.DOUBLEBUF) 可以消除渲染过程中的闪烁问题,提供更平滑的显示效果。
"""
gameDisplay = pygame.display.set_mode((image_w,image_h), pygame.HWSURFACE | pygame.DOUBLEBUF)

# 通过将黑色作为参数传递给 fill() 方法,可以将窗口的背景色设置为黑色。
gameDisplay.fill((0,0,0))

#blit() 方法用于将一个图像绘制到另一个图像上,或者绘制到游戏显示窗口上。第一个参数 renderObject.surface 是要绘制的图像,第二个参数 (0, 0) 是绘制的位置,表示图像在游戏显示窗口上的左上角的坐标。
gameDisplay.blit(renderObject.surface, (0,0))

#刷新pygame窗口画面
pygame.display.flip()

9、更新pygame画面及处理车辆控制的键盘事件

# Game loop
crashed = False

while not crashed:
    # 等待同步
    world.tick()
    # 按帧更新渲染的Camera画面
    gameDisplay.blit(renderObject.surface, (0,0))
    pygame.display.flip()
    # 处理车辆控制请求
    controlObject.process_control()
    # 获取pygame事件
    for event in pygame.event.get():
        # If the window is closed, break the while loop
        if event.type == pygame.QUIT:
            crashed = True
        # 获得pygame控制车辆键盘事件
        controlObject.parse_control(event)

10、退出pygame结束仿真

# Stop camera and quit PyGame after exiting game loop
ego_vehicle.destory()
camera.stop()
pygame.quit()

三、运行Carla和pygame

1、打开Carla客户端

  • 运行carla客户端并设置低帧率模式./CarlaUE4.sh -prefernvidia -quality-level=Low -benchmark -fps=15

在这里插入图片描述

2、运行完整代码

  • 我是用vs code直接运行的,没有vs code可以直接python3 来运行,把代码复制进空的py文件,然后运行。
import carla
import random
import pygame
import numpy as np

# 连接到客户端并检索世界对象
client = carla.Client('localhost', 2000)
world = client.get_world()

# 在同步模式下设置模拟器
settings = world.get_settings()
settings.synchronous_mode = True # Enables synchronous mode
settings.fixed_delta_seconds = 0.05
world.apply_settings(settings)

# 以同步模式设置TM
traffic_manager = client.get_trafficmanager()
traffic_manager.set_synchronous_mode(True)

# 设置种子,以便必要时行为可以重复
traffic_manager.set_random_device_seed(0)
random.seed(0)

# 获取地图的刷出点
spawn_point = random.choice(world.get_map().get_spawn_points())

# 生成车辆并设置自动驾驶
vehicle_bp = random.choice(world.get_blueprint_library().filter('*vehicle*'))
ego_vehicle = world.spawn_actor(vehicle_bp, spawn_point)
ego_vehicle.set_autopilot(True)

traffic_manager.ignore_lights_percentage(ego_vehicle, 100)

# 渲染对象来保持和传递PyGame表面
class RenderObject(object):
    def __init__(self, width, height):
        init_image = np.random.randint(0,255,(height,width,3),dtype='uint8')
        self.surface = pygame.surfarray.make_surface(init_image.swapaxes(0,1))

# 相机传感器回调,将相机的原始数据重塑为2D RGB,并应用于PyGame表面
def pygame_callback(data, obj):
    img = np.reshape(np.copy(data.raw_data), (data.height, data.width, 4))
    img = img[:,:,:3]
    img = img[:, :, ::-1]
    obj.surface = pygame.surfarray.make_surface(img.swapaxes(0,1))

# 控件对象来管理车辆控件
class ControlObject(object):
    def __init__(self, veh):

        # 控制参数来存储控制状态
        self._vehicle = veh
        self._steer = 0
        self._throttle = False
        self._brake = False
        self._steer = None
        self._steer_cache = 0
        self._control = carla.VehicleControl()

    # 检查PyGame窗口中的按键事件
    # 定义控件状态
    def parse_control(self, event):
        if event.type == pygame.KEYDOWN:
            if event.key == pygame.K_RETURN:
                self._vehicle.set_autopilot(False)
            if event.key == pygame.K_UP:
                self._throttle = True
            if event.key == pygame.K_DOWN:
                self._brake = True
            if event.key == pygame.K_RIGHT:
                self._steer = 1
            if event.key == pygame.K_LEFT:
                self._steer = -1
        if event.type == pygame.KEYUP:
            if event.key == pygame.K_UP:
                self._throttle = False
            if event.key == pygame.K_DOWN:
                self._brake = False
                self._control.reverse = False
            if event.key == pygame.K_RIGHT:
                self._steer = None
            if event.key == pygame.K_LEFT:
                self._steer = None

    # 处理当前控制状态,改变控制参数
    def process_control(self):

        if self._throttle: 
            self._control.throttle = min(self._control.throttle + 0.01, 0.5)
            self._control.gear = 1
            self._control.brake = False
        elif not self._brake:
            self._control.throttle = 0.0

        if self._brake:
            # 如果在汽车静止时按住向下箭头,则切换到倒车
            if self._vehicle.get_velocity().length() < 0.01 and not self._control.reverse:
                self._control.brake = 0.0
                self._control.gear = 1
                self._control.reverse = True
                self._control.throttle = min(self._control.throttle + 0.1, 0.5)
            elif self._control.reverse:
                self._control.throttle = min(self._control.throttle + 0.1, 0.5)
            else:
                self._control.throttle = 0.0
                self._control.brake = min(self._control.brake + 0.3, 1)
        else:
            self._control.brake = 0.0

        if self._steer is not None:
            if self._steer == 1:
                self._steer_cache += 0.03
            if self._steer == -1:
                self._steer_cache -= 0.03
            min(0.7, max(-0.7, self._steer_cache))
            self._control.steer = round(self._steer_cache,1)
        else:
            if self._steer_cache > 0.0:
                self._steer_cache *= 0.2
            if self._steer_cache < 0.0:
                self._steer_cache *= 0.2
            if 0.01 > self._steer_cache > -0.01:
                self._steer_cache = 0.0
            self._control.steer = round(self._steer_cache,1)

        # Ápply小车的控制参数
        self._vehicle.apply_control(self._control)

# 初始化安装在车辆后面的摄像头
camera_init_trans = carla.Transform(carla.Location(x=-5, z=3), carla.Rotation(pitch=-20))
camera_bp = world.get_blueprint_library().find('sensor.camera.rgb')
camera = world.spawn_actor(camera_bp, camera_init_trans, attach_to=ego_vehicle)

# 用PyGame回调启动camera
camera.listen(lambda image: pygame_callback(image, renderObject))

# 设置渲染画面size
image_w = 800
image_h = 600

# 为渲染和车辆控制实例化对象
renderObject = RenderObject(image_w, image_h)
controlObject = ControlObject(ego_vehicle)

# 初始化显示
pygame.init()
gameDisplay = pygame.display.set_mode((image_w,image_h), pygame.HWSURFACE | pygame.DOUBLEBUF)
# 填充黑色背景
gameDisplay.fill((0,0,0))
gameDisplay.blit(renderObject.surface, (0,0))
pygame.display.flip()


# 循环执行
crashed = False

while not crashed:
    # 等待同步
    world.tick()
    # 按帧更新渲染的Camera画面
    gameDisplay.blit(renderObject.surface, (0,0))
    pygame.display.flip()
    # 处理车辆控制请求
    controlObject.process_control()
    # 获取pygame事件
    for event in pygame.event.get():
        # If the window is closed, break the while loop
        if event.type == pygame.QUIT:
            crashed = True
        # 获得pygame控制车辆键盘事件
        controlObject.parse_control(event)

# 结束
ego_vehicle.destory()
camera.stop()
pygame.quit()

3、运行效果展示

  • 回车键:关闭自动驾驶 上键:前进 下键:后退 左键:往左 右键:往右
  • 注意需要关闭自动驾驶才能通过键盘去控制车辆上下左右运行。
    在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/550289.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

软件设计师 试题二做题技巧

问题一&#xff1a;去需求分析中找相应的联系类型 问题二先看缺少的属性是不是er图中的实体&#xff0c;如果是实体文中就会有相关的信息&#xff0c;如果没有就对应的去图中找&#xff0c;转化为关系模式对应的联系也要转化为关系模式 如果是一对一就把联系归并到任意一方实…

JS 实现拖拽元素的功能

JS 实现拖拽元素的功能 这篇笔记比较短&#xff0c;主要过一遍 draggable 的事件。 首先简单看一下 HTML 实现&#xff1a; <!DOCTYPE html> <html lang"en"><head><meta charset"UTF-8" /><meta http-equiv"X-UA-Comp…

【小程序修改说明】分享朋友的,请忽略!

一、把js文件夹复制到根目录&#xff0c;跟pages同一目录 二、把下面的代码放到app.wxss最下面 代码 import "/js/font-awesome.wxss"; import "/js/font-awesome_small.wxss"; 三、ywym.wxml修改 1、ywym.wxml的最上边加上如下代码 <navigator url&…

【Linux】线程详解之线程控制

文章目录 POSIX线程库创建线程线程ID及进程地址空间布局线程等待pthread_join 线程终止pthread_exit函数pthread_cancel函数 线程分离理解pthread库 POSIX线程库 POSIX线程&#xff08;英语&#xff1a;POSIX Threads&#xff0c;常被缩写为Pthreads&#xff09;是POSIX的线程标…

若依源码解析:DataScopeAspect实现数据范围的控制

文章目录 源代码使用场景界面操作SysDeptServiceImplSysUserServiceImplSysUserMapperDataScope定义 代码解析Aspect和Component不同的数据权限类型Before通知处理数据范围的方法 源代码 Aspect Component public class DataScopeAspect {/*** 全部数据权限*/public static fi…

Python潮流周刊#2:Rust让Python再次伟大

△点击上方“Python猫”关注 &#xff0c;回复“1”领取电子书 这里记录每周值得分享的 Python 及通用技术内容&#xff0c;部分为英文&#xff0c;已在小标题注明。&#xff08;本期标题取自其中一则分享&#xff0c;不代表全部内容都是该主题&#xff0c;特此声明。&#xff…

【Linux Network】I/O多路转接之select

目录 1. 初识select 1.1 select函数原型 1.2 理解select执行过程 1.3 socket就绪条件 1.4 select的特点 1.5 select优缺点 2. 基于select的多人聊天程序 server源代码&#xff1a; client的登录&#xff1a; 结果演示&#xff1a; Linux Network&#x1f337; 1. 初识select 系…

C++初阶--C++入门之基础学习

0.前言 C是一门非常好的编程语言&#xff0c;但可能在学习C的过程中会遇到很多困难。人们常说 “一个人走得很快&#xff0c;一群人会走的更远”&#xff0c; 所以就让我们一起攻坚克难&#xff0c;一起征服C吧&#xff01;从本章开始&#xff0c;我们将开始C的基础学习&#x…

Linux简介及基础操作

1.Linux的作用&#xff1a; 商业服务器基本都是linux的、开源软件都先支持linux、大数据分析&#xff0c;机器学习首选linux、整个互联网地基基本由linux支撑起来。如&#xff1a; 生活中的手机是基于linux二次开发的&#xff0c;还有路由器也是基于linux开发的。 2.Linux是什…

acwing提高--多源BFS+最小步数模型+双端队列广搜

多源BFS 1.矩阵距离 题目https://www.acwing.com/problem/content/description/175/ #include<bits/stdc.h> using namespace std; #define x first #define y second typedef pair<int,int> PII; const int N1010; char g[N][N]; int dist[N][N]; PII q[N*N];…

【轻量化网络系列(2)】MobileNetV2论文超详细解读(翻译 +学习笔记+代码实现)

前言 上一篇我们介绍了MobileNetV1&#xff0c;主要是将普通Conv转换为dw和pw&#xff0c;但是在dw中训练出来可能会很多0&#xff0c;也就是depthwise部分得到卷积核会废掉&#xff0c;即卷积核参数大部分为0&#xff0c;因为权重数量可能过少&#xff0c;再加上Relu激活函数…

稳定币是个好生意

* * * 原创&#xff1a;刘教链 * * * 本月早些时候&#xff0c;市值第一的稳定币发行商Tether公布了其一季度的储备和盈利数据[1]。不能说是亮眼&#xff0c;只能说是非常亮眼。就看几个亮点吧&#xff1a; 1. 一季度净利润14.8亿美元&#xff0c;是2022年四季度的两倍多&…

关于Java中的抽象类注意事项

文章目录 &#x1f3c6;文章导读&#x1f342;抽象类的定义&#x1f342;抽象类的特性&#x1f342;总结&#xff1a;面试题普通类和抽象类有哪些区别&#xff1f;抽象类能使用final继承吗&#xff1f; &#x1f3c6;文章导读 在本篇文章中&#xff0c;对抽象类进行了一个详细的…

c++学习——c与c++const修饰的变量的区别

c语言下const修饰的变量 1、c语言下const修饰的变量都有空间 2. c语言的const修饰的全局变量具有外部链接属性 07 const修饰的变量.c #define _CRT_SECURE_NO_WARNINGS #include <stdio.h> #include <string.h> #include <stdlib.h>const int a 10;//常…

1. Linux环境搭建及问题解决方案

本文介绍了Linux环境搭建的过程以及遇到的问题和解决方案&#xff0c;并且介绍了常用的Linux命令. 一、Linux环境搭建 整体所需的环节 安装VMware安装Linux &#xff08;这边我选的是Server版本&#xff09;安装配置Samba&#xff08;Samba是一种Linux和Windows之间进行文件共…

二层环路详解:交换机环路产生的过程和原因

前言&#xff1a; 在了解环路之前得先了解交换机的工作原理&#xff0c;当然交换机的基本工作原理其实非常简单&#xff0c;只有“单播转发与泛洪转发”、“交换机MAC地址表”这两个&#xff01;其他的如vlan&#xff0c;生成树等也是在此基础上增加的&#xff0c;弥补交换机基…

初始Linux的基本操作

上篇博客中&#xff0c;我介绍了关于Linux的相关概念&#xff0c;让我们初步的了解到Linux的重要性&#xff0c;在这篇博客中我会再讲一些Linux操作系统的理解。 一.操作系统 我们知道Linux是一个操作系统&#xff0c;而操作系统操作系统(英语&#xff1a;Operating System&…

[深度好文]10张图带你轻松理解关系型数据库系统的工作原理

[深度好文]10张图带你轻松理解关系型数据库系统的工作原理 原文(欢迎关注)&#xff1a;https://mp.weixin.qq.com/s/CNCfWRpv8QlICGvZkLG4Jw 尽管数据库在我们应用程序中扮演着储存几乎所有状态的关键角色&#xff0c;但人们对其运行原理的了解通常仅停留在较为浅显的层面&…

跟我一起使用 compose 做一个跨平台的黑白棋游戏(4)移植到compose-jb实现跨平台

前言 在上一篇文章中&#xff0c;我们已经实现了游戏的所有界面和逻辑代码&#xff0c;并且在 Android 上已经可以正常运行。 这篇文章我们将讲解如何将其从使用 jetpack compose 修改为使用 compose-jb 从而实现跨平台。 老规矩&#xff0c;先看效果图&#xff1a; 可以看到…

063:cesium设置带边界线材质(material-7)

第063个 点击查看专栏目录 本示例的目的是介绍如何在vue+cesium中设置带边界折线材质,请参考源代码,了解PolylineOutlineMaterialProperty的应用。 直接复制下面的 vue+cesium源代码,操作2分钟即可运行实现效果. 文章目录 示例效果配置方式示例源代码(共89行)相关API参考…