OrangePi PC 玩Linux主线内核踩坑之旅(五)之简易版的贪吃蛇

可不可以用香橙派来玩游戏呢?当然可以,今天用它来玩贪吃蛇!

一、终端版的贪吃蛇

在踩坑之旅(四)中,我们介绍了设置声卡的两款工具alsamixer和amixer,很显然alsamixer这种图形化的界面更加人性化,更适合人机互动,那这个界面是如何实现的呢?亲自动手完成变身MP3播放器的同学会发现,alsamixer用到了一个依赖包ncurses。Ncurses是一个能提供功能键定义(快捷键),屏幕绘制以及基于文本终端的图形互动功能的动态库[1]。Ncurses可以:

  • 只要您喜欢,您可以使用整个屏幕
  • 创建和管理一个窗口
  • 使用8种不同的彩色
  • 为您的程序提供鼠标支持
  • 使用键盘上的功能键

有心的同学可能会记得,在踩坑之旅(一)中make menuconfig也用到了ncurses库,没错,就是我们看到的那个配置界面。事实上,make menuconfig最终修改了buildroot-2020.11目录下的.config文件,这个文件就是个文本文档,可以手工修改,但还是用make menuconfig更直观。

由此看来,Ncurses可以实现终端界面下的图形互动,虽然略显简陋。本文介绍的贪吃蛇就是Ncurses的一个典型应用。当然,这里不是要讲贪吃蛇的具体算法实现,我们直接使用他人开源的代码。下载地址:

https://codeload.github.com/CoinsJack/my_snake_game/zip/master

将代码下载到桌面上。对源代码感兴趣的同学可以访问作者的博客:

https://www.cnblogs.com/litran/p/10640893.html

将代码包解压到桌面,此时可以看到目录中有个可执行文件hungry_snake,这个文件是电脑端运行的文件,你可以双击运行体验一下。我们需要重新编译可在香橙派运行的可执行文件,也就是传说中的交叉编译。在终端进入该目录,输入如下命令对代码进行编译

dpmicro@dpmicro-PC:~/Desktop/my_snake_game-master$ arm-linux-gnueabihf-gcc -o snake main.c snake.c -L ../buildroot-2020.11/output/target/usr/lib -lncurses -I ../buildroot-2020.11/output/host/arm-buildroot-linux-gnueabihf/sysroot/usr/include/

将生成的snake文件传送到香橙派上

dpmicro@dpmicro-PC:~/Desktop/my_snake_game-master$ scp snake root@192.168.1.10:/root
root@192.168.1.10's password: 
snake                                      100%   23KB 681.9KB/s   00:00

在香橙派的SSH命令中输入

# ls
snake
# ./snake

按下字母e键开始游戏吧。

画面颜色不好看?我们来改代码,很简单。打开main.c,将第25行至32行修改如下

  init_pair(1,COLOR_GREEN,COLOR_BLACK);//前景色,背景色
  init_pair(2,COLOR_BLACK,COLOR_WHITE);

  attron(COLOR_PAIR(1));//增加一行,其余不变
  print_snake_logo(stdscr);
  mvprintw(LINES-1,COLS-20,"Producted by Jack");

看看效果

画面颜色太单调?继续改main.c

#include <unistd.h>
#include <stdlib.h>
#include "snake.h"
#include <ncurses.h>
#include "wellcome.h"
#include <pthread.h>


int ch;
int GAMEOVER;

int main()
{
    initscr();
    start_color();
    keypad(stdscr,TRUE);
    curs_set(0);
    noecho();
    halfdelay(4);
    init_pair(1, COLOR_GREEN,COLOR_BLACK);
    init_pair(2, COLOR_BLACK,COLOR_WHITE);
//增加7种前景色,加上背景色黑色,一共支持8种颜色
    init_pair(4, COLOR_RED,   COLOR_BLACK);
    init_pair(5, COLOR_GREEN, COLOR_BLACK);
    init_pair(6, COLOR_YELLOW,COLOR_BLACK);
    init_pair(7, COLOR_BLUE,  COLOR_BLACK);
    init_pair(8, COLOR_MAGENTA,COLOR_BLACK);
    init_pair(9, COLOR_CYAN,  COLOR_BLACK);
    init_pair(10, COLOR_WHITE, COLOR_BLACK);

    attron(COLOR_PAIR(1));
    print_snake_logo(stdscr);
    mvprintw(LINES-1,COLS-20,"Producted by Jack");



    mvprintw(LINES-4,(COLS-21)/2,"|-> START");
    printw(" (? for help)");


    refresh();
    int choice;
    char goout = 0;
    while (!goout)
        {

            switch (choice = getch() )
                {
                case 'q':
                    endwin();
                    return 0;
                case 'e':
                    goout = 1;
                    break;
                }

        }


    Psnake jack = init_snake();
    init_show_snake(jack);
    ch = mvgetch(LINES-1,0);
    srand((unsigned)time(0));

    for (int i = 0; i<10; i++)
        putfood(jack);
    refresh();
    int noout = 1;
    int c = 0;
    int i=4;//颜色循环
    while(noout)
        {
            attron(COLOR_PAIR(i));//使用i指向的颜色
            ch = getch();
            switch (ch)
                {

                case KEY_RIGHT:
                    c = snake_go_right(jack);
                    if ( c == -1)
                        {
                            noout = 0;
                            GAMEOVER = 1;
                            break;
                        }
                    continue;

                case KEY_DOWN :
                    c = snake_go_down(jack);
                    if ( c == -1)
                        {
                            noout = 0;
                            GAMEOVER = 1;
                            break;
                        }
                    continue;

                case KEY_LEFT:
                    c = snake_go_left(jack);
                    if ( c == -1)
                        {
                            noout = 0;
                            GAMEOVER = 1;
                            break;
                        }
                    continue;

                case KEY_UP:
                    c = snake_go_up(jack);
                    if ( c == -1)
                        {
                            noout = 0;
                            GAMEOVER = 1;
                            break;
                        }
                    continue;
                case 'q':
                    game_over(jack);
                    endwin();
                    return 0;
                }

            if (jack->righting)
                {
                    c = snake_go_right(jack);
                    if (c == -1)
                        {
                            GAMEOVER = 1;
                            break;
                        }
                }
            else if (jack->lefting)
                {
                    c = snake_go_left(jack);
                    if (c == -1)
                        {
                            GAMEOVER = 1;
                            break;
                        }

                }
            else if(jack->uping)
                {
                    c = snake_go_up(jack);
                    if (c == -1)
                        {
                            GAMEOVER = 1;
                            break;
                        }

                }
            else if(jack->downing)
                {
                    c = snake_go_down(jack);
                    if (c == -1)
                        {
                            GAMEOVER = 1;
                            break;
                        }

                }

            if (food_num < 30)
                putfood(jack);
            mvprintw(LINES-1,2,"-- socre %d --",jack->length);
            attroff(COLOR_PAIR(i));
            refresh();
            i++;
            if(i==11)i=4;//颜色循环结束
        }

    if(GAMEOVER)
        game_over(jack);

    refresh();

    endwin();
    return 0;
}

漂亮多了吧?好似满天繁星

二、接个显示器玩贪吃蛇

如此好玩的游戏只能借助SSH才能使用实在是不舒服,我们来给香橙派接个显示器。香橙派自带HDMI和AV模拟输出,而我只有VGA接口的显示器,所以使用HDMI转VGA再连接显示器。开机上电,uboot启动内核后好像没有画面了,只剩一个光标在左上角闪烁,这是怎么回事?刚通电的时候,可以看到显示器上有uboot的启动信息,说明是可以通过HDMI输出画面的,问题出在哪里呢?回到SSH,查看uboot传递给内核的启动参数

# cat /proc/cmdline 
console=ttyS0,115200 earlyprintk root=/dev/mmcblk0p2 rootwait

可以看到,console参数只配置了串口,也就是说内核启动、初始化的信息只会输出到串口。这个配置在哪里修改呢?打开buildroot-2020.11/board/orangepi/orangepi-pc目录下的boot.cmd文件可以看到,这些参数均属于环境变量bootargs

setenv fdt_high ffffffff

setenv bootargs console=ttyS0,115200 earlyprintk root=/dev/mmcblk0p2 rootwait

fatload mmc 0 $kernel_addr_r zImage
fatload mmc 0 $fdt_addr_r sun8i-h3-orangepi-pc.dtb

bootz $kernel_addr_r - $fdt_addr_r

如果我们将第三行修改为

setenv bootargs console=ttyS0,115200 console=tty0 earlyprintk root=/dev/mmcblk0p2 rootwait

然后回到buildroot-2020.11目录下,先make再烧录镜像,你会发现,完全没有效果,哈哈。这是因为uboot实际读取的是boot.scr,然而make命令并没有生成新的文件。我们来手工生成一下,终端打开buildroot-2020.11/board/orangepi/orangepi-pc目录

dpmicro@dpmicro-PC:~/Desktop/buildroot-2020.11/board/orangepi/orangepi-pc$ ../../../output/build/uboot-2020.07/tools/mkimage -A arm -O linux -T script -C none -a 0 -e 0 -n "OrangePi Boot Script" -d boot.cmd boot.scr
Image Name:   OrangePi Boot Script
Created:      Fri Jan  1 18:00:47 2021
Image Type:   ARM Linux Script (uncompressed)
Data Size:    249 Bytes = 0.24 KiB = 0.00 MiB
Load Address: 00000000
Entry Point:  00000000
Contents:
   Image 0: 241 Bytes = 0.24 KiB = 0.00 MiB
dpmicro@dpmicro-PC:~/Desktop/buildroot-2020.11/board/orangepi/orangepi-pc$ ls
boot.cmd  boot.scr  genimage.cfg  linux.fragment  readme.txt

将刚刚生成的boot.scr传送到香橙派TF卡的第一分区,也就是那个FAT32格式的分区。我们先将该分区进行挂载,然后用scp传过去

# mount /dev/mmcblk0p1 /mnt/
# ls /mnt/
boot.scr                  sun8i-h3-orangepi-pc.dtb  zImage
dpmicro@dpmicro-PC:~/Desktop/buildroot-2020.11/board/orangepi/orangepi-pc$ scp boot.scr root@192.168.1.10:/mnt
root@192.168.1.10's password: 
boot.scr                                        100%  313    55.3KB/s   00:00

好,现在重启香橙派。可以看到,在屏幕上也显示了内核启动的各项信息。但是,启动完并不能登陆啊,纯粹只是显示内核打印的信息而已。。。

突然回想起踩坑之旅(一)当中的一个配置

这个配置项用于配置系统启动后终端模式的启动方式,当然啦,这里配置的是串口,那这个配置实际修改了哪个文件呢?是/etc/inittab,我们来查看一下

# /etc/inittab
#
# Copyright (C) 2001 Erik Andersen <andersen@codepoet.org>
#
# Note: BusyBox init doesn't support runlevels.  The runlevels field is
# completely ignored by BusyBox init. If you want runlevels, use
# sysvinit.
#
# Format for each entry: <id>:<runlevels>:<action>:<process>
#
# id        == tty to run on, or empty for /dev/console
# runlevels == ignored
# action    == one of sysinit, respawn, askfirst, wait, and once
# process   == program to run

# Startup the system
::sysinit:/bin/mount -t proc proc /proc
::sysinit:/bin/mount -o remount,rw /
::sysinit:/bin/mkdir -p /dev/pts /dev/shm
::sysinit:/bin/mount -a
::sysinit:/sbin/swapon -a
null::sysinit:/bin/ln -sf /proc/self/fd /dev/fd
null::sysinit:/bin/ln -sf /proc/self/fd/0 /dev/stdin
null::sysinit:/bin/ln -sf /proc/self/fd/1 /dev/stdout
null::sysinit:/bin/ln -sf /proc/self/fd/2 /dev/stderr
::sysinit:/bin/hostname -F /etc/hostname
# now run any rc scripts
::sysinit:/etc/init.d/rcS

# Put a getty on the serial port
ttyS0::respawn:/sbin/getty -L  ttyS0 115200 vt100 # GENERIC_SERIAL

# Stuff to do for the 3-finger salute
#::ctrlaltdel:/sbin/reboot

# Stuff to do before rebooting
::shutdown:/etc/init.d/rcK
::shutdown:/sbin/swapoff -a
::shutdown:/bin/umount -a -r

# Put a getty on the serial port这一句已经很明显了,我们来增加一句,修改如下

# Put a getty on the serial port
ttyS0::respawn:/sbin/getty -L  ttyS0 115200 vt100 # GENERIC_SERIAL
tty0::respawn:/sbin/getty tty0 0

大体格式上照抄,但tty0实际上是虚拟的设备,不是串口,也就没有波特率,这里写0,其实任何值都无所谓。这样就将终端同时在串口和屏幕上启动。保存文件然后重启。

这样就达到了在屏幕上显示终端的目的。事实上,上文的boot.cmd可以不修改,不过踩坑嘛,没坑还踩啥。没错,不用揉眼睛,这个SNAKE不是彩色的,我也很奇怪,明明是可以显示颜色的呀,有知道的小伙伴请留言哦。

参考文献

[1]https://www.oschina.net/p/ncurses

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

大鹏集成

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值