基于TwinCAT结构化文本的类Simon记忆游戏按键校验及计数问题咨询
TwinCAT结构化文本开发Simon游戏:按键校验与错误统计方案
你用TwinCAT ST做Simon记忆游戏遇到的这两个问题,其实核心是要先把游戏的序列管理和用户输入追踪逻辑理清楚。我结合你现有的代码片段,给你调整和补充下实现思路:
一、先完善必要的变量定义
首先得补充几个核心变量来管理游戏状态、序列存储和用户输入:
VAR // 原有变量 iRandomNumber: DINT; fb_delay : ton; Run: BOOL; GameSpeed: TIME:= T#2S; io.blue: bool; io.red: bool; // 假设还有其他颜色LED,根据你的硬件补充 io.green: bool; io.yellow: bool; io.start: bool; io.blue_btn: bool; // 对应蓝色按键 io.red_btn: bool; io.green_btn: bool; io.yellow_btn: bool; // 新增游戏管理变量 aGameSequence: ARRAY[1..20] OF DINT; // 存储随机生成的LED序列,最多20步 aUserInput: ARRAY[1..20] OF DINT; // 存储用户按下的按键序列 iCurrentStep: DINT := 0; // 当前游戏序列的长度 iShowIndex: DINT := 0; // 序列展示时的当前索引 iUserInputIndex: DINT := 0; // 用户当前输入的索引 bInputPhase: BOOL := FALSE; // 是否处于用户输入阶段 iErrorCount: DINT := 0; // 累计错误次数 iLastWrongInputCount: DINT := 0; // 记录上次错误时已按下的按键数 fb_btn_delay: ton; // 按键消抖用延时块 END_VAR
二、校验按键操作与亮起的LED是否一致
Simon游戏的逻辑是先展示随机序列→再让用户按顺序重复,所以要拆分「序列展示阶段」和「用户输入阶段」,在输入阶段逐个对比用户按键和序列值:
1. 生成并展示序列
当启动游戏后,先生成随机序列,逐个点亮对应LED:
// 启动游戏逻辑 IF io.start THEN Run := TRUE; iCurrentStep := 1; // 初始化第一步序列 iShowIndex := 0; // 生成第一个随机数(1-4对应四个颜色) iRandomNumber := RANDOM(1,4); aGameSequence[iCurrentStep] := iRandomNumber; fb_delay(in:=TRUE, PT:=GameSpeed); // 启动展示延时 END_IF // 序列展示逻辑:逐个点亮LED IF Run AND NOT bInputPhase THEN IF iShowIndex < iCurrentStep THEN // 熄灭所有LED,准备点亮当前序列的LED io.blue := FALSE; io.red := FALSE; io.green := FALSE; io.yellow := FALSE; iShowIndex := iShowIndex + 1; // 根据序列值点亮对应LED CASE aGameSequence[iShowIndex] OF 1: io.blue := TRUE; 2: io.red := TRUE; 3: io.green := TRUE; 4: io.yellow := TRUE; END_CASE; fb_delay(in:=TRUE, PT:=GameSpeed); // 启动当前LED的点亮延时 ELSIF fb_delay.Q THEN // 序列展示完毕,切换到用户输入阶段 io.blue := FALSE; io.red := FALSE; io.green := FALSE; io.yellow := FALSE; bInputPhase := TRUE; iUserInputIndex := 0; END_IF; END_IF
2. 用户输入校验
在输入阶段,捕捉按键动作并实时和序列对应位置对比:
// 用户输入处理:仅在输入阶段响应按键 IF bInputPhase THEN // 按键消抖处理:避免单次按键被识别多次 IF fb_btn_delay.Q THEN fb_btn_delay(in:=FALSE); END_IF; // 捕捉各颜色按键输入 IF io.blue_btn AND NOT fb_btn_delay.Q THEN iUserInputIndex := iUserInputIndex + 1; aUserInput[iUserInputIndex] := 1; fb_btn_delay(in:=TRUE, PT:=T#200MS); ELSIF io.red_btn AND NOT fb_btn_delay.Q THEN iUserInputIndex := iUserInputIndex + 1; aUserInput[iUserInputIndex] := 2; fb_btn_delay(in:=TRUE, PT:=T#200MS); ELSIF io.green_btn AND NOT fb_btn_delay.Q THEN iUserInputIndex := iUserInputIndex + 1; aUserInput[iUserInputIndex] := 3; fb_btn_delay(in:=TRUE, PT:=T#200MS); ELSIF io.yellow_btn AND NOT fb_btn_delay.Q THEN iUserInputIndex := iUserInputIndex + 1; aUserInput[iUserInputIndex] := 4; fb_btn_delay(in:=TRUE, PT:=T#200MS); END_IF; // 每次输入后校验是否匹配序列 IF iUserInputIndex > 0 THEN // 输入错误的情况 IF aUserInput[iUserInputIndex] <> aGameSequence[iUserInputIndex] THEN bInputPhase := FALSE; Run := FALSE; iLastWrongInputCount := iUserInputIndex; // 记录错误时已按下的按键数 iErrorCount := iErrorCount + 1; // 可添加错误提示逻辑,比如闪烁所有LED // 输入正确且完成当前序列的情况 ELSIF iUserInputIndex = iCurrentStep THEN bInputPhase := FALSE; iCurrentStep := iCurrentStep + 1; // 生成新的随机数加入序列 iRandomNumber := RANDOM(1,4); aGameSequence[iCurrentStep] := iRandomNumber; // 准备展示新序列 iShowIndex := 0; fb_delay(in:=TRUE, PT:=GameSpeed); END_IF; END_IF; END_IF
三、按键错误时统计已按下的按键数量
上面的代码已经包含了这个逻辑:当检测到用户输入和序列不一致时,把当前的iUserInputIndex赋值给iLastWrongInputCount,这个变量就精准记录了错误发生时用户已经按下的按键数量。
比如用户在第3步输入错误,iUserInputIndex此时是3,那么iLastWrongInputCount就会被设为3,你可以把这个值输出到HMI、存储到变量或者用于后续的游戏逻辑判断。
额外提醒
- 按键消抖是必须的,不然会出现单次按键被识别多次的误判
- TwinCAT的
RANDOM函数需要初始化种子,不然每次启动游戏都会生成相同的序列 - 可以根据游戏进度动态调整
GameSpeed,比如每完成5步就缩短展示时长,增加游戏难度
内容的提问来源于stack exchange,提问作者Hoang Ho
相关产品推荐
相关产品推荐

