触摸屏虚拟摇杆:XY 轴输出、通道按钮、死区、波纹动效、自定义外观。
VirtualJoystick 是移动端虚拟摇杆控件:
• Base + Knob:底盘 + 摇杆头,触摸拖动
• XY 输出:-1.0 ~ 1.0 归一化向量
• DeadZone:死区过滤微小抖动
• Channel:附加通道按钮(如跳跃/攻击)
• Ripple:触摸波纹动效
• BuiltinAtlas:内置纹理图集或自定义 Surface
| 函数 | 说明 |
|---|---|
int xuiVirtualJoystickCreate(pCtx, ppW, pDesc) | 创建摇杆 |
int xuiVirtualJoystickSetChange(pW, onChange, pUser) | 变化回调 |
int xuiVirtualJoystickSetValue(pW, fX, fY, bNotify) | 编程设值 |
int xuiVirtualJoystickReset(pW, bNotify) | 归零 |
float xuiVirtualJoystickGetX(pW) | X 轴值 |
float xuiVirtualJoystickGetY(pW) | Y 轴值 |
float xuiVirtualJoystickGetMagnitude(pW) | 幅度 |
float xuiVirtualJoystickGetAngle(pW) | 角度 |
int xuiVirtualJoystickSetChannel(pW, iCh, bPressed, fVal, bNotify) | 通道 |
int xuiVirtualJoystickSetMetrics(pW, fRadius, fKnob, fDeadZone) | 尺寸/死区 |
int xuiVirtualJoystickSetColors(pW, iBase, iBaseAct, iKnob, ...) | 颜色 |
gcc -O2 -I. -o ch188.exe ch188_main.c src\xui_*.c build\xge.lib -lm -lws2_32 -liphlpapi -lgdi32 -luser32 -lshell32 -lopengl32 -lole32 -lwinmm -lavrt
.\ch188.exe
xui_virtual_joystick_desc_t desc = { 0 };
desc.onChange = OnJoystickChange;
xui_widget pJoy;
xuiVirtualJoystickCreate(pCtx, &pJoy, &desc);
// 半径 80px,摇杆头 30px,死区 10%
xuiVirtualJoystickSetMetrics(pJoy, 80.0f, 30.0f, 0.1f);
// 半透明配色
xuiVirtualJoystickSetColors(pJoy,
0xFFFFFF40, 0xFFFFFF60,
0xFFFFFF80, 0xFFFFFFC0,
0x4FD8C240, 0x4FD8C280, 0x80808040);
// 游戏循环中读取
float fX = xuiVirtualJoystickGetX(pJoy);
float fY = xuiVirtualJoystickGetY(pJoy);
// 移动角色:player.x += fX * speed * dt;将以下代码保存为 ch188_main.c,编译后运行即可看到虚拟摇杆控制效果:
#include "xge.h"
#include "xui.h"
#include <stdio.h>
static xui_context g_pCtx;
static xui_widget g_pJoy;
static int onFrame(void* pUser) {
(void)pUser;
xgeClear(0x1A1A2EFF);
xui_vec2_t pos;
xgeMouseGet(&pos.x, &pos.y);
xuiInputPointerMove(g_pCtx, pos.x, pos.y);
if (xgeMouseDown(XGE_MOUSE_LEFT))
xuiInputPointerDown(g_pCtx, pos.x, pos.y, 0);
else
xuiInputPointerUp(g_pCtx, pos.x, pos.y, 0);
// 读取摇杆值
float fX = xuiVirtualJoystickGetX(g_pJoy);
float fY = xuiVirtualJoystickGetY(g_pJoy);
if (fX != 0.0f || fY != 0.0f)
printf("Joystick: %.2f, %.2f\r", fX, fY);
xuiUpdate(g_pCtx, xgeGetDelta());
xuiLayout(g_pCtx);
xuiRender(g_pCtx, NULL, NULL, 0);
return XGE_OK;
}
int main(void) {
xge_desc_t d = { 0 };
d.iWidth = 400; d.iHeight = 400;
d.sTitle = "ch188 - VirtualJoystick";
d.iFlags = XGE_INIT_VSYNC;
xgeInit(&d);
xuiCreate(&g_pCtx);
extern const xui_proxy_t tXgeProxy;
xui_proxy_t tXgeProxy = xuiProxyXge();
xuiSetProxy(g_pCtx, &tXgeProxy);
xuiSetViewportSize(g_pCtx, 400, 400);
xui_virtual_joystick_desc_t jdesc = { 0 };
xuiVirtualJoystickCreate(g_pCtx, &g_pJoy, &jdesc);
// 半径 80,摇杆头 30,死区 10%
xuiVirtualJoystickSetMetrics(g_pJoy, 80.0f, 30.0f, 0.1f);
// 半透明配色
xuiVirtualJoystickSetColors(g_pJoy,
0xFFFFFF40, 0xFFFFFF60,
0xFFFFFF80, 0xFFFFFFC0,
0x4FD8C240, 0x4FD8C280, 0x80808040);
xui_widget pRoot = xuiGetRootWidget(g_pCtx);
xuiWidgetAddChild(pRoot, g_pJoy);
xuiWidgetSetRect(g_pJoy, (xui_rect_t){0, 0, 400, 400});
xgeRun(onFrame, NULL);
xuiDestroy(g_pCtx);
xgeUnit();
return 0;
}