Download src/agentcade/effects.py from left0ver/WatcherRobot-com.left0ver.agentcade: direct link, hf CLI and curl.
- Browser
- Download file 4.11 kB
-
https://huggingface.co/spaces/left0ver/WatcherRobot-com.left0ver.agentcade/resolve/main/src/agentcade/effects.py
- Command line
-
hf download hf://spaces/left0ver/WatcherRobot-com.left0ver.agentcade/src/agentcade/effects.py
-
curl -L -o effects.py https://huggingface.co/spaces/left0ver/WatcherRobot-com.left0ver.agentcade/resolve/main/src/agentcade/effects.py
4.11 kB
| """常用的设备效果:点头、摇头、颜文字表情、逐笔画图和进度条。 | |
| 这些是游戏端的辅助函数,把一个效果拼成几次机器人 SDK 调用,通过游戏的 self.robot 发出, | |
| 和直接调用 SDK 一样:用到动作要在 SKILL.md 里声明 motion,用到屏幕要声明 expression.runtime.v3。 | |
| """ | |
| import asyncio | |
| import logging | |
| import math | |
| import time | |
| from agentcade.expressions import expression_vector, segmented_progress_vector | |
| from agentcade.eyes import HIDDEN_EYES, STANDBY_EYES | |
| from agentcade.robot_calls import RobotProxy | |
| logger = logging.getLogger(__name__) | |
| # 点头、摇头依次转到的(水平角, 俯仰角),从正前方出发再回到正前方。 | |
| GESTURES = { | |
| "nod": ((90, 115), (90, 105), (90, 115)), | |
| "shake_head": ((90, 115), (105, 115), (75, 115), (90, 115)), | |
| } | |
| # 颜文字表情的图案编号。 | |
| EXPRESSIONS = {"happy": "009", "sorry": "141"} | |
| # 逐笔画图时每一步之间的间隔秒数。 | |
| DRAW_INTERVAL = 0.16 | |
| async def gesture(robot: RobotProxy, name: str) -> None: | |
| """点头(nod)或摇头(shake_head)。主 Agent 等动作做完再执行后面的指令和播报。""" | |
| for pan, tilt in GESTURES[name]: | |
| await robot.motion.move_to(pan_deg=pan, tilt_deg=tilt, duration_ms=350, profile="ease_in_out") | |
| async def show(robot: RobotProxy, path: str) -> None: | |
| """屏幕只显示这个矢量图案,眼睛藏起来。""" | |
| await robot.expression_runtime.start("standby", **HIDDEN_EYES, tag="none", custom_vector_path=path) | |
| async def standby(robot: RobotProxy) -> None: | |
| """屏幕回到待机的眼睛。""" | |
| await robot.expression_runtime.start("standby", **STANDBY_EYES) | |
| async def react(robot: RobotProxy, expression: str, gesture_name: str, *, then: str | None = None) -> None: | |
| """对用户的回答做出反应:显示开心(happy)或抱歉(sorry)的颜文字,同时点头或摇头; | |
| 做完后屏幕换回 then 这个图案,没有时回到待机的眼睛。""" | |
| await show(robot, expression_vector(EXPRESSIONS[expression])) | |
| await gesture(robot, gesture_name) | |
| await (show(robot, then) if then else standby(robot)) | |
| async def draw(robot: RobotProxy, steps: list[str], *, interval: float = DRAW_INTERVAL) -> None: | |
| """逐笔画出图案:每一项是一步的完整矢量路径,最后一步留在屏幕上。""" | |
| await show(robot, steps[0]) | |
| for path in steps[1:]: | |
| await asyncio.sleep(interval) | |
| await robot.expression_runtime.update(custom_vector_path=path) | |
| class Progress: | |
| """显示进度条,预计 seconds 秒走完,用在耗时的操作外面: | |
| async with Progress(self.robot, 30): | |
| ... | |
| 操作正常完成时进度条走满;出错或者被取消时停在当前位置。 | |
| """ | |
| def __init__(self, robot: RobotProxy, seconds: float): | |
| if not math.isfinite(seconds) or seconds <= 0: | |
| raise ValueError("预计秒数必须是有限正数") | |
| self.robot, self.seconds = robot, seconds | |
| self.task: asyncio.Task | None = None | |
| async def __aenter__(self): | |
| await show(self.robot, segmented_progress_vector(0)) | |
| self.task = asyncio.create_task(self.animate()) | |
| return self | |
| async def animate(self) -> None: | |
| started = time.monotonic() | |
| for step in range(1, 10): | |
| await asyncio.sleep(max(0.0, started + step * self.seconds / 10 - time.monotonic())) | |
| await show(self.robot, segmented_progress_vector(step)) | |
| async def __aexit__(self, kind, error, traceback): | |
| self.task.cancel() | |
| # 用 wait 等它停下,不吞掉外面对游戏的取消。 | |
| await asyncio.wait({self.task}) | |
| if not self.task.cancelled() and self.task.exception() is not None: | |
| # 进度条只是提示,显示失败不影响正在做的事。 | |
| logger.warning("进度条没有显示完整", exc_info=self.task.exception()) | |
| if kind is None: | |
| await show(self.robot, segmented_progress_vector(10)) | |
| await asyncio.sleep(0.25) | |
| return False | |