ncursesでちょっとしたゲームを作ってみた話

ncursesでちょっとしたゲームを作ってみた話

BEAN

まえがき

C言語からプログラミング始めた人「プログラミングはつまらない」 こういう話をたまに聞きます.

これについての理由の一つにC言語の入門書が扱う内容を勉強したところでそれですぐにできるソフトウェアが黒い画面に文字だけというビジュアルが地味すぎるというところがまぁすぐ思い浮かびますね.

こんなんだったらプログラムじゃないですが, HTML勉強した方がビジュアル面ではもうちょっと派手なことができます.

あ, もちろんそうでない人もいます. アルゴリズムを駆使して問題解いていく競技プログラミングをする人(競プロerと言うそうです。変態ですね)とかです.(Cを使うかどうかは…

じゃC言語は数値や文字列を返すだけなのかというとそういうわけでもありません.

ncursesというライブラリを用いればもうちょい動きのある物は作れます. と言っても普段私たちが使うようなクールでUsefulなものではありません. そこまでいくと作るのにもっとスキルがいります. しかしC言語でstaticな出力よりは幾分面白いと思います.

さてこのぐらいまえがき書いておけばいいでしょう. では→→→

ncursesでちょっとしたゲームを作ってみた話

筆者の環境

  • MacBook Pro (Retina, 13-inch, Early 2015) macOS High Sierra (10.13.2)
  • gcc 4.2.1
  • 数杯(崇拝)のコーヒ
  • Ed Sheeranの音楽

ncursesとは

こういう説明はTerminal.appを起動してその無機質な黒文字 白背景の画面に man ncurses と打ち込んでやれば教えてくれます. もしくはSiriに聞いてみてもいいかもしれません. ちなみにこういう返答をくれます.

ほぅ. Wikipediaに書いてることみてもわからな時はGoogle画像検索にかけて出てきたイメージでイメージしましょう. だいたいわかりますね?

簡単に言うと, 知ってるC言語の知識とncursesで提供されるAPI(関数)を組み合わせるとCUIチックな出力がGUIチックに変わるということです.

と言っても難しいのでいくつかの僕のものより素晴らしい例を出しましょう. 以下のものはncuesesで作られたゲームです.

  • C言語で2048を作る
  • C言語でシンプルすぎるブロック崩しを書いた

うーんしゅごい 実は今回作ってみたSnakeGameは2048を見てやってみようかという気になったものなんですよね.

SnakeGame

そもそもSnakeGameとは何かを提示しておきましょう. SnakeGameはそれが正式名称かどうかは知らないですが、ヘビを操作して食べ物を食べていくあれです.

説明が難しいのですが、実際のゲームとしてはGoogle Assistant に「ゲームがしたい」と話しかけるといくつかのゲームができるようになっているのですが、そのうちの"スネーク"と題されているゲームです.

一度やってみると「ああこれか」と言う人と「初見」と言う人半々でいるぐらい(個人的統計で)にはメジャかマイナか判別に困るゲームです.

本題

プログラム関係のことを書いていきましょう.

上記の通りncurseと言うライブラリを使うわけですが, これ結構Webで日本語の資料が見つかるんですよね.

  • ncurses
  • curses による端末制御
  • 雑記録 - NCURSES Programming HOWTO

この辺読めばだいたい読める/書けるようになります.

今度は自分が作ったSnakeGameの話題に移ります. 今も開発し続けているので(developブランチで開発進行中)release v0.1.0 で話を進めます. GitHubとしてはここになります.

開発の話

この頃はまだどのように分割して開発を進めるかがよくわかってない→わかる気がするへの変化期だったのでexternとかその辺のやつはノリで書いてます. 参考にしないでください.(developブランチのはもうちょっとマシでしょう。動かないかもしれませんが…)

流れ

以降でヘビと突然出てきますが, これはゲーム内で出てくるヘビっぽいアレです. 一度git cloneしてmakeして./snakegameしてみるとわかります. あと特に言わずに出てくる関数とかマクロっぽいものはfunctions.hかncursesのものです. functions.h見てなければggってみればいい感じにたどり着くんじゃないでしょうか.

ちなみにアイキャッチ画像はここで話すv0.1.0では出てきません() –––アイキャッチ画像は得てして自身を誇張するものだ.

main.c

main.cファイルが大きな流れを書いてるのでちょっと見てみましょう.

int key = ERR;
int oldKey = KEY_DOWN;

keyとoldKeyは何がキーボードから入力されたかが格納される変数になっています. keyは今のキーの状態, oldKeyは前のキーの状態を格納します. 今ここで書いてるのは初期値を設定しています.

main関数に入って…

bool gameOver = false;
bool stopGame = false;

ゲームのイベント発生を管理/格納する変数です. これstdbool.hインクルードしてないですね.動くんだ…と今思いました. gameOverはゲームオーバーしたような状態ならtrueが入るように, stopGameはゲームの一時中断時にtrueが入るようになっています.

initGameScreen();
initGameConfig();

stopGame = gameStartScreen();

ここではゲームの初期化を行なっています. この辺の自作関数はfunctions.hに説明を書いてるのでそっちを参照してください.

if(key == ERR || areKeysRev(key, oldKey) == true) {
            
    key = oldKey;
            
}

ここでは初期(ゲーム開始)状態とキーボードにおいて逆(上と下は逆, 左と右は逆)を押された時は前の状態のキーを現在のキーであるとしています. 初期状態ではヘビがどっちの方向に行けばいいかどうかが不明(キーボードから入力がない状態から始まる)のでoldKeyに入ってるKEY_DOWNを現在のキーにしています. 現在のキーにもともと入れておけばいいんじゃないか…と言われそうですが多分(覚えてない)key = oldKeyを一回しか書きたくなかったんじゃないですかね. areKeysRevをしてる理由ですがヘビは後ろ方向には瞬時に動けない, という制約です. これやっちゃうと簡単になりますし, 突然蛇の尻尾が頭になってしまいますしね.

switch (key) {
            case 'w' : /* 上キーと同じ処理 */
            case KEY_UP : gameOver = crawl(UP);    break;
                
            case 's' : /* 下キーと同じ処理 */
            case KEY_DOWN : gameOver = crawl(DOWN);  break;
                
            case 'a' : /* 左キーと同じ処理 */
            case KEY_LEFT : gameOver = crawl(LEFT);  break;
                
            case 'd' : /* 右キーと同じ処理 */
            case KEY_RIGHT : gameOver = crawl(RIGHT); break;
                
            case 'q' : /* 一時中断 */
                stopGame = pauseGame();
                key = oldKey;
                break;
                
            default :
                key = oldKey; break;
        }

これは入力されたキーに対してどの動作を行うかです. wかKEY_UPが押されればヘビを上方向に進路変更しなくてはならない…ということです. crawl関数はこのゲームにおいて核になるような関数です. qが押された時はpause画面を出力してゲームを一時停止させます.この時再開するならqキーの前に押されていたキーがゲーム再開後に押されていたように動作させています. 特に何も動作しないキーが押されたときはそのキーを破棄して前まで押されていた有効なキーに置き換えます.

if(gameOver) {
        
        gameOverScreen();
        
    }
    
    killSnake(snake);
    
    endwin();

ゲームオーバでwhileを抜けているならゲームオーバー画面を出力します. そのあとヘビのメモリ(initGameConfig()関数で確保している)を解放してウインドウを決して終了です.

functions.c / functions.h

このファイルの中に色々書いてのでそれ読んでもらえると…

色々やばそうなコードがありますがそのうちに治ってるでしょう…?

自分で作った関数の中ではcrawl関数が一番見てて好きですね. というよりアロー演算子が好き."->"

あとがき

内容的に薄いなぁと思いますが、割とソース読めばええやん感が増してきてるので…

開発するのにエディタ/IDEをVScodeとXcodeを使って見たのですが肌に合うのはVScodeでした. 自作関数とか変数の予測がXcodeにはない…?(C言語において)のと定義してるファイル/場所に飛ぶというのがXcodeではうまくいかなかった感じがあります. あと全体的にエディタの色とかそういう面でもVScodeの方が好きです. モニタの白い色はちょっと疲れる.

というあたりで締めたいと思います. 多分次の投稿は早いです. 年内には出しておきたい(現在12/28…

SnakeGameのGitHub

comments powered by Disqus