ubuntu 如何安装blender
官网blender.org下载tar.xz压缩文件
tar -xvf xxx.tar.xz如何启动blender,命令行输入:
blender
如何在blender中安装pygame模块
需要找到blender中的python解释器路径import sys
print(sys.executable)然后在终端terminal中使用以下命令
$ “xxxx/python” -m pip install pygame
在ubuntu系统下,使用blender执行脚本时,看不到日志信息。可以使用logging 模块调试程序。import logging
logging.basicConfig(level=logging.DEBUG)
logger = logging.getLogger(__name__)logger.debug("详细调试信息")
logger.info("常规信息")
logger.warning("警告信息")
logger.error("错误信息")
这时候打印的信息,在控制台显示
ubuntu 使用blender如果出现卡死,可以使用如下命令,强制关闭blender 界面
pkill -9 blender
# 安装测试工具
sudo apt install jstest-gtk joystick# 检测手柄
jstest /dev/input/js0
终端会出现如图所示的结果,最下面一行是 摇杆和开关的状态。如果不清楚映射关系,可以操控手柄,观察数值变化。
根据测试,遥控手柄的键位映射关系如下
摇杆Axes :
左横向:Axes 4
左纵向:Axes 1
右横向:Axes 3
右纵向:Axes 0
Axes 2 未知。手柄上没有控件对应Axes 2.
按钮 buttons:
SWA: 0
SWB:2
SWC:3
手柄上左上角的旋钮未知。
也可以通过 jstest-gtk图形工具,测试手柄的键位映射。
选择 properties.
晃动摇杆,或者切换SWA/B/C 开关,即可观测数值变化。
通过Axes 和 按钮 buttons, 即可完成后续的开发工作。
以下是关于游戏手柄的操作说明。
1.确保游戏手柄通过usb接口连接到ubuntu 系统;
2.确保blender 安装了pygame 模块,并可以正常使用;
pygame可以自动识别游戏手柄。
手柄摇杆映射参考:
left_stick_x = joystick.get_axis(4) # 左摇杆 X 轴,横轴
left_stick_y = joystick.get_axis(1) # 左摇杆 Y 轴, 纵轴
right_stick_x = joystick.get_axis(3) # 右摇杆 X 轴,横轴
right_stick_y = joystick.get_axis(0) # 右摇杆 Y 轴,纵轴
开发日志
执行脚本的时候,容易 发生闪退。
频繁发生以下现象
以下代码可以测试摇杆输出值,但是会引发blender is not responding 问题。
import bpy
import math
import random
import time
import mathutils
import pygameimport logging
logging.basicConfig(level=logging.DEBUG)
logger = logging.getLogger(__name__)logger.debug("详细调试信息")
logger.info("常规信息")
logger.warning("警告信息")
logger.error("错误信息")import pygame# 初始化 Pygame
pygame.init()
pygame.joystick.init()# 检测手柄数量
joystick_count = pygame.joystick.get_count()
if joystick_count == 0:print("未检测到手柄!")exit()# 初始化第一个手柄
joystick = pygame.joystick.Joystick(0)
joystick.init()
print(f"手柄名称: {joystick.get_name()}")
print(f"摇杆数量: {joystick.get_numaxes() // 2}") # 每个摇杆占 2 个轴(X/Y)# 主循环
running = True
while running:for event in pygame.event.get():if event.type == pygame.QUIT:running = False# 获取摇杆状态(不依赖事件,直接读取)left_stick_y = joystick.get_axis(1) # 左摇杆 X 轴(通常 -1 左,1 右)right_stick_x = joystick.get_axis(3) left_stick_x = joystick.get_axis(4) # have some problemright_stick_y = joystick.get_axis(0)# 打印摇杆值(保留 2 位小数)print(f"左摇杆: X={left_stick_x:.2f}, Y={left_stick_y:.2f} | 右摇杆: X={right_stick_x:.2f}, Y={right_stick_y:.2f}", end="\r")pygame.time.delay(50) # 降低输出频率pygame.quit()
以下是测试程序和功能说明
import bpy
import math
import random
import time
import mathutils
import pygameimport logging
logging.basicConfig(level=logging.DEBUG)
logger = logging.getLogger(__name__)logger.debug("详细调试信息")
logger.info("常规信息")
logger.warning("警告信息")
logger.error("错误信息")class GamepadModalOperator(bpy.types.Operator):bl_idname = "object.gamepad_modal_operator"bl_label = "Gamepad Modal Control"_timer = None_joystick = Nonedef modal(self, context, event):# 首先处理Blender事件(如ESC键退出)if event.type in {'ESC'}:return self.cancel(context)# 只在TIMER事件中处理手柄输入,避免过于频繁的更新if event.type == 'TIMER':# 处理Pygame事件队列pygame.event.pump()# 获取所有Pygame事件但不处理(避免阻塞)for py_event in pygame.event.get():# 处理按钮事件if py_event.type == pygame.JOYBUTTONDOWN:print(f"按钮 {py_event.button} 按下")if py_event.button == 0: # A按钮示例print("back")# 指定物体的名称object_name = "drone" # 替换为你的物体名称obj = bpy.data.objects.get(object_name)if obj:# 定义移动向量(局部坐标系)move_vector = mathutils.Vector((0, 0, -0.2)) # 沿着局部坐标系的 X 轴移动 1 个单位# (x,z,y)# 将局部坐标系的向量转换为世界坐标系world_move_vector = obj.matrix_world @ move_vector - obj.matrix_world.translation# 更新物体的位置obj.location += world_move_vectorif py_event.button == 3: # A按钮示例print("forward") object_name = "drone" obj = bpy.data.objects.get(object_name)if obj:# 定义移动向量(局部坐标系)move_vector = mathutils.Vector((0, 0, 0.2)) # 沿着局部坐标系的 X 轴移动 1 个单位# (x,z,y)# 将局部坐标系的向量转换为世界坐标系world_move_vector = obj.matrix_world @ move_vector - obj.matrix_world.translation# 更新物体的位置obj.location += world_move_vectorelse:print(f"物体 '{object_name}' 未找到!") if py_event.type == pygame.JOYBUTTONUP:print(f"按钮 {py_event.button} 释放")# 方向键(hat)事件if event.type == pygame.JOYHATMOTION:print(f"方向键 {event.hat} 值: {event.value}")if event.value== (-1,0):print("left turn!!!")if event.value== (1,0):print("right turn!!!")if event.value== (0,1):print("up turn!!!")if event.value== (0,-1):print("down turn!!!")# 直接读取轴状态(更高效的方式)
# if self._joystick:
# # 获取左摇杆状态(轴0和1)
# axis_x = self._joystick.get_axis(0)
# axis_y = self._joystick.get_axis(1)
#
# # 应用死区过滤
# deadzone = 0.2
# move_speed = 0.1
#
# if context.active_object:
# if abs(axis_x) > deadzone:
# context.active_object.location.x += axis_x * move_speed
# if abs(axis_y) > deadzone:
# context.active_object.location.y -= axis_y * move_speed # Y轴反转
#
# # 获取右摇杆状态(轴2和3)
# axis_rx = self._joystick.get_axis(2)
# axis_ry = self._joystick.get_axis(3)
#
# if abs(axis_rx) > deadzone:
# context.active_object.rotation_euler.z -= axis_rx * 0.05
# if abs(axis_ry) > deadzone:
# context.active_object.rotation_euler.x += axis_ry * 0.05return {'PASS_THROUGH'}def execute(self, context):# 初始化Pygamepygame.init()pygame.joystick.init()# 检查手柄连接if pygame.joystick.get_count() == 0:self.report({'ERROR'}, "未检测到手柄设备")return {'CANCELLED'}# 初始化第一个手柄self._joystick = pygame.joystick.Joystick(0)self._joystick.init()print(f"已连接手柄: {self._joystick.get_name()}")# 设置定时器,控制更新频率wm = context.window_managerself._timer = wm.event_timer_add(0.04, window=context.window) # 约50FPSwm.modal_handler_add(self)return {'RUNNING_MODAL'}def cancel(self, context):# 清理资源if self._timer:wm = context.window_managerwm.event_timer_remove(self._timer)if hasattr(self, '_joystick') and self._joystick:self._joystick.quit()pygame.quit()print("手柄控制已退出")return {'CANCELLED'}def register():bpy.utils.register_class(GamepadModalOperator)def unregister():bpy.utils.unregister_class(GamepadModalOperator)# 测试运行
if __name__ == "__main__":register()bpy.ops.object.gamepad_modal_operator('INVOKE_DEFAULT')
以下脚本可以正常输出。
import bpy
import math
import random
import time
import mathutils
import pygameimport logging
logging.basicConfig(level=logging.DEBUG)
logger = logging.getLogger(__name__)logger.debug("详细调试信息")
logger.info("常规信息")
logger.warning("警告信息")
logger.error("错误信息")class GamepadModalOperator(bpy.types.Operator):bl_idname = "object.gamepad_modal_operator"bl_label = "Gamepad Modal Control"_timer = None_joystick = Nonedef modal(self, context, event):# 首先处理Blender事件(如ESC键退出)if event.type in {'ESC'}:return self.cancel(context)# 只在TIMER事件中处理手柄输入,避免过于频繁的更新if event.type == 'TIMER':# 处理Pygame事件队列pygame.event.pump()# 获取所有Pygame事件但不处理(避免阻塞)for py_event in pygame.event.get():# 处理按钮事件if py_event.type == pygame.JOYBUTTONDOWN:print(f"按钮 {py_event.button} 按下")if py_event.button == 0: # A按钮示例print("back")# 指定物体的名称object_name = "drone" # 替换为你的物体名称obj = bpy.data.objects.get(object_name)if obj:# 定义移动向量(局部坐标系)move_vector = mathutils.Vector((0, 0, -0.2)) # 沿着局部坐标系的 X 轴移动 1 个单位# (x,z,y)# 将局部坐标系的向量转换为世界坐标系world_move_vector = obj.matrix_world @ move_vector - obj.matrix_world.translation# 更新物体的位置obj.location += world_move_vectorif py_event.button == 3: # Y按钮示例print("forward") object_name = "drone" obj = bpy.data.objects.get(object_name)if obj:# 定义移动向量(局部坐标系)move_vector = mathutils.Vector((0, 0, 0.2)) # 沿着局部坐标系的 X 轴移动 1 个单位# (x,z,y)# 将局部坐标系的向量转换为世界坐标系world_move_vector = obj.matrix_world @ move_vector - obj.matrix_world.translation# 更新物体的位置obj.location += world_move_vectorelse:print(f"物体 '{object_name}' 未找到!") if py_event.type == pygame.JOYBUTTONUP:print(f"按钮 {py_event.button} 释放")# 方向键(hat)事件if event.type == pygame.JOYHATMOTION:print(f"方向键 {event.hat} 值: {event.value}")if event.value== (-1,0):print("left turn!!!")if event.value== (1,0):print("right turn!!!")if event.value== (0,1):print("up turn!!!")if event.value== (0,-1):print("down turn!!!")# 直接读取轴状态(更高效的方式)
# if self._joystick:
# # 获取左摇杆状态(轴0和1)
# axis_x = self._joystick.get_axis(0)
# axis_y = self._joystick.get_axis(1)
#
# # 应用死区过滤
# deadzone = 0.2
# move_speed = 0.1
#
# if context.active_object:
# if abs(axis_x) > deadzone:
# context.active_object.location.x += axis_x * move_speed
# if abs(axis_y) > deadzone:
# context.active_object.location.y -= axis_y * move_speed # Y轴反转
#
# # 获取右摇杆状态(轴2和3)
# axis_rx = self._joystick.get_axis(2)
# axis_ry = self._joystick.get_axis(3)
#
# if abs(axis_rx) > deadzone:
# context.active_object.rotation_euler.z -= axis_rx * 0.05
# if abs(axis_ry) > deadzone:
# context.active_object.rotation_euler.x += axis_ry * 0.05return {'PASS_THROUGH'}def execute(self, context):# 初始化Pygamepygame.init()pygame.joystick.init()# 检查手柄连接if pygame.joystick.get_count() == 0:self.report({'ERROR'}, "未检测到手柄设备")return {'CANCELLED'}# 初始化第一个手柄self._joystick = pygame.joystick.Joystick(0)self._joystick.init()print(f"已连接手柄: {self._joystick.get_name()}")# 设置定时器,控制更新频率wm = context.window_managerself._timer = wm.event_timer_add(0.04, window=context.window) # 约50FPSwm.modal_handler_add(self)return {'RUNNING_MODAL'}def cancel(self, context):# 清理资源if self._timer:wm = context.window_managerwm.event_timer_remove(self._timer)if hasattr(self, '_joystick') and self._joystick:self._joystick.quit()pygame.quit()print("手柄控制已退出")return {'CANCELLED'}def register():bpy.utils.register_class(GamepadModalOperator)def unregister():bpy.utils.unregister_class(GamepadModalOperator)# 测试运行
if __name__ == "__main__":register()bpy.ops.object.gamepad_modal_operator('INVOKE_DEFAULT')