[经验分享] 【平头哥RVB2601创意应用开发】@fxyc87 RVB2601-贪吃蛇游戏

fxyc87   2022-3-25 20:32 楼主

这个贪吃蛇只实现了基本功能,有一些错误检测未加入,我后边再再补充。

绘图只是自己写了几句代码简单绘图,没有使用LVGL。

按键由于板上只有2个,所以按键用串口控制,有一个上位机,打开上位机后,使用电脑键盘来控制游戏。

串口控制使用了cli,见我的上一个贴子:【平头哥RVB2601创意应用开发】@fxyc87 RVB2601-CLI(命令提示符)测试

先上效果:

微信图片_20220325202343.jpg

上位机软件,使用小众的aardio编写的,这个工具写的单EXE程序非常小。

企业微信截图_20220325202444.png


游戏主代码:

#include <stdlib.h>
#include "oled.h"
#include "ui.h"
#include "snake.h"
#include <aos/aos.h>
#include "aos/cli.h"

//游戏状态
EnumSnakeStatus SnakeStatus;
//蛇体长度
volatile int SnakeLength;
//蛇体数据
Point SnakeData[250] ;
//蛇身移动前尾部,要清除
Point SnakeTail;
//食物上次位置 ,要清除
Point LastFood;
//死亡位置 
Point DeathPoint;
//食物位置 
Point FoodPoint;
//随机数种子
uint8_t RandFood;
//游戏初始速度 ,
int SnakeSpeed=600;
//每增加一个长度,速度+30mS
const int SnakeSpeedStep=30;
//最近一次按键
EnumMoveDirection LastKey=KeyNone;
//调试模式 此模式下移动受按键控制,不自动走
int SnakeDebug=0;
//cli回调指令结构
const struct cli_command SnakeCommand={
	.name="snake",
	.help="Direction Operation,Command=Left/Right/Up/Down",
	.function=SnakeKey
};

//初始化
void SnakeInit(){
	//初始长度1,位置=屏幕中央
	
	SnakeData[0].y=SNAKE_HEIGHT/2;
	SnakeData[0].x=SNAKE_WIDTH/2;
	
	SnakeData[1].y=SNAKE_HEIGHT/2;
	SnakeData[1].x=SNAKE_WIDTH/2+1;	
	SnakeLength=2;

	//初始cli命令
	aos_cli_register_command(&SnakeCommand);

	SnakeStatus=None;
	DeathPoint.x=0xff;
	FoodPoint.x=0xff;
	SnakeTail.x=0xff;
	//随机产生食物
	SnakeFood();
}
//随机产生食物,产生食物不能在蛇体上,需要在空白处
void SnakeFood(){	
	FoodPoint.x=0xff;	
	//伪随机数
	RandFood+=RandFood*17+3;
	//随机数不能大于蛇身总长度
	RandFood=RandFood%sizeof(SnakeData);
	//查找空白处,查找N次以达到随机效果
	printf("food:%d",RandFood);
	uint8_t count=0,flag;
	for(int x=0;x<SNAKE_WIDTH;x++){
		for(int y=0;y<SNAKE_HEIGHT;y++){
			flag=0;
			for(int i=0;i<=SnakeLength;i++){
				if(SnakeData[i].x==x && SnakeData[i].y==y){	//不能在蛇身上
					flag=1;
					break;
				}
			}
			if(flag==0 && count==RandFood){
				FoodPoint.x=x;
				FoodPoint.y=y;
			}
			count++;
		}
	}
	if(FoodPoint.x==0xff){
		FoodPoint.x=SNAKE_WIDTH/2;
		FoodPoint.y=SNAKE_HEIGHT/2;		
	}
}
//键盘按下键, cli回调函数
static void SnakeKey(char *outbuf, int len, int argc, char **argv){
	if(argc<=1){
		//cli_printf("Error:Direction not specified");		//未指定方向
		strcpy(outbuf,"Error:Direction not specified");	//未指定方向
		return;
	}
	//strcasecmp strcmp
	if(strcasecmp(argv[1],"left") == 0) {
		LastKey=Left;
	}else if(strcasecmp(argv[1],"right") == 0) {
		LastKey=Right;
	}else if(strcasecmp(argv[1],"up") == 0) {
		LastKey=Up;
	}else if(strcasecmp(argv[1],"down") == 0) {
		LastKey=Down;
	}else if(strcasecmp(argv[1],"pause") == 0) {
		SnakeStatus=Pause;
		return;
	}else if(strcasecmp(argv[1],"debug") == 0) {
		SnakeDebug=1;
	}else{
		strcpy(outbuf,"Invalid direction");
		return;
	}
	SnakeStatus=Run;
}

//移动,4个方向
void SnakeMove(EnumMoveDirection dir){
	if(dir==KeyNone)
		return;
	//保存未移动前的位置
	//DeathPoint
	//第一个移动,后一个=前一个
	int li=SnakeLength-1;
	SnakeTail=SnakeData[li];	//保存最后一个,绘图时要清除
	for(int i=li;i>0;i--){
		SnakeData[i]=SnakeData[i-1];
	}
	switch(dir){
		case Left:
			SnakeData[0].x--;
			break;
		case Right:
			SnakeData[0].x++;
			break;
		case Up:
			SnakeData[0].y--;
			break;			
		case Down:
			SnakeData[0].y++;
			break;		
	}
	if(SnakeDebug)
		LastKey=None;
	
	//所有检查是否有超出屏幕的 x<0 x>width y<0 y>heigh
	for(int i=0;i<SnakeLength;i++){
		if(SnakeData[i].x<0 || SnakeData[i].x>=SNAKE_WIDTH ||
			SnakeData[i].y<0 || SnakeData[i].y>=SNAKE_HEIGHT ){
				SnakeStatus=Death;
				return;
			}
	}
	//检查与自身是否相撞
	
	//吃子,身体变长
	if(SnakeData[0].x==FoodPoint.x && SnakeData[0].y==FoodPoint.y){
		//todo:检查身体达到极限,显示win画面
		SnakeData[SnakeLength]=SnakeTail;
		SnakeTail.x=0xff;
		SnakeFood();
		//检查尾部到达当前食物点,加1
		SnakeLength++;
		//速度加快
		if(SnakeSpeed>=80)
			SnakeSpeed-=SnakeSpeedStep;
	}

}


//绘制当前游戏,定时调用,游戏速度*4倍速调用
void SnakeDraw(){
	static int draw_count=0;
	//画蛇身
	//蛇身10次画1次
	for(int i=0;i<SnakeLength;i++){
		SnakeDrawPoint(SnakeData[i].x,SnakeData[i].y,1);
	}
	//清除上次蛇尾
	if(SnakeTail.x!=0xff)
		SnakeDrawPoint(SnakeTail.x,SnakeTail.y,0);
	//画食物 闪烁
	if(draw_count % 2==0){
		SnakeDrawFood(FoodPoint.x,FoodPoint.y);		
	}else{
		SnakeDrawPoint(FoodPoint.x,FoodPoint.y,0);			
	}

	//画碰撞(闪烁)
	
	//更新
	oled_reflesh();
	
	draw_count++;
	if(draw_count>10)
		draw_count=0;
}
//画食物
void SnakeDrawFood(uint8_t xx,uint8_t yy){
	//4*4 去除4个角点
	SnakeDrawPoint(xx,yy,1);
	int x=xx*4;
	int y=yy*4;
	oled_draw_point(y,x,0);
	oled_draw_point(y,x+3,0);
	oled_draw_point(y+3,x,0);
	oled_draw_point(y+3,x+3,0);
}
//画蛇的大点 4像素一个点 ,x,y,c颜色
void SnakeDrawPoint(uint8_t xx,uint8_t yy,uint8_t c){
	int x=xx*4;
	int y=yy*4;
	int temp=y;
	int x2=x+SNAKE_SIZE;
	int y2=y+SNAKE_SIZE;
	for(;x<x2;x++){
		for(y=temp;y<y2;y++){
			oled_draw_point(y,x,c);	
		}
	}
}

//游戏循环
void SnakeLoop(){
	SnakeInit();
	while(1){
		//触发移动
		SnakeMove(LastKey);
		//更新 todo:只更新变化部分
		SnakeDraw();
		if(SnakeDebug)
			aos_msleep(100);
		else
			aos_msleep(SnakeSpeed);
			
	}	
}

注释都比较全,不介绍了

在main主程序调用SnakeLoop主循环即可。

int main(void)
{
    board_yoc_init();
    
	
    oled_init();	

	SnakeLoop();
	
    return 0;
}

 

以下是源程序:

fxyc87_game.7z (9.28 MB)
(下载次数: 7, 2022-3-25 20:29 上传)

 

上位机程序EXE:

CH2601_Direction_Operation.7z (509.13 KB)
(下载次数: 4, 2022-3-25 20:29 上传)

 

上位机程序源码Aardio创建:

import fonts.fontAwesome;
import win.ui;
import sio;
import sys.comPort;
import  win.timer;
/*DSG{{*/
mainForm = win.form(text="CH2601_Direction_Operation";right=489;bottom=229)
mainForm.add(
btnOpen={cls="button";text='\uF04B 开始';left=390;top=15;right=475;bottom=65;font=LOGFONT(name='FontAwesome');z=3};
cmbCom={cls="combobox";left=15;top=40;right=375;bottom=65;edge=1;items={};mode="dropdown";z=1};
labDirection={cls="static";text='\uF0E7';left=300;top=100;right=460;bottom=175;font=LOGFONT(h=-35;name='FontAwesome');transparent=1;z=5};
labInfo={cls="static";text="等待打开通信串口";left=15;top=115;right=260;bottom=170;transparent=1;z=7};
labTime={cls="static";left=305;top=200;right=470;bottom=220;transparent=1;z=8};
static={cls="static";text='\uF044 通信串口选择';left=15;top=15;right=115;bottom=35;font=LOGFONT(name='FontAwesome');transparent=1;z=2};
static2={cls="static";text="方向控制说明:
可以通过方向键或WSAD键来控制游戏。
fxyc87 xa87@live.cn";left=15;top=175;right=270;bottom=230;transparent=1;z=4};
static4={cls="static";text="状态:";left=15;top=85;right=75;bottom=110;transparent=1;z=6}
)
/*}}*/

var coms=sys.comPort.list();
for(i,v in coms){
	mainForm.cmbCom.add(v.portName + " 【" + v.friendlyName+"】");
}
mainForm.cmbCom.selIndex = sys.comPort.find("CP210x")[["index"]] or 1;
mainForm.labDirection.hide=true;

var port;
var status=0;		//0默认 1打开串口未开始 2已开始
mainForm.btnOpen.oncommand = function(id,event){
	if(status>0){
		mainForm.btnOpen.text='\uF04B 开始';
		status=0;
		if(port){
			port.close();
		}
		mainForm.labInfo.text="已关闭通信端口";
		mainForm.labDirection.hide=true;
		mainForm.cmbCom.disabled=false;
		return;
	}
	var sel=string.split(mainForm.cmbCom.selText," ")[1];
	port,err=sio.port(sel);
	if(port){
		port.setBaudRate(115200);
		mainForm.labDirection.hide=false;
		mainForm.btnOpen.text='\uF2D4 关闭';
		mainForm.labInfo.text="可以按任意方向键开始游戏了";
		mainForm.cmbCom.disabled=true;
		status=1;
		mainForm.btnOpen.setFocus();
	}else{
		mainForm.labInfo.text='通信端口打开失败:\n'+err;
	}
}

var timer=win.timer(mainForm,100);
timer.enable();

timer.onTimer = function(){
	if(status>0){
		mainForm.btnOpen.setFocus();			
	}
	mainForm.labTime.text=tostring(time.now());
}


mainForm.btnOpen.wndproc = function(hwnd,message,wParam,lParam){
	if(message==0x0100){
		if(wParam==100 || wParam==65){		//\uF060
			//mainForm.labDirection.text=tostring(wParam);
			port.write('snake left\r\n');
			mainForm.labDirection.text='\uF060';
		}elseif(wParam==102 || wParam==68){
			mainForm.labDirection.text='\uF061';
			port.write('snake right\r\n');
		}elseif(wParam==104 || wParam==87){
			port.write('snake up\r\n');
			mainForm.labDirection.text='\uF062';
		}elseif(wParam==98 || wParam==83||wParam==101){
			port.write('snake down\r\n');
			mainForm.labDirection.text='\uF063';
		}else{
			mainForm.labDirection.text=tostring(wParam);
		}
	}
}

mainForm.show();
return win.loopMessage()

 

回复评论 (2)

不错,显示屏用起来了。

点赞  2022-3-26 07:47
绘图只是自己写了几句代码简单绘图,没有使用LVGL。  按键由于板上只有2个,所以按键用串口控制,有一个上位机,打开上位机后,使用电脑键盘来控制游戏
点赞  2022-3-26 09:55
电子工程世界版权所有 京B2-20211791 京ICP备10001474号-1 京公网安备 11010802033920号
    写回复