Arduinoのシンプルなゲームコンソール

エントリー



日の光の中で、そして夢の中で、私は自分で規制されたテレビのセットトップボックスを作成するというアイデアを思いつきました。 実際、当時、豊かで飽和した無線工学の世界が私の前に開かれました。 以前は電子機器の本格的な開発に取り組んでいなかったため、私の選択はよりシンプルなオプション-Arduinoとその最も一般的なモデル-Unoでした。









作業計画



1.ライブラリを扱う

2.ビデオ出力ボードをはんだ付けします

3.コードを書く

4.体を切る



類似のプロジェクトの場合、最終的な外部コンポーネントは特に重要ではありません。



ステップ1.何が何であるかを理解する



数十分の必死のグーグル検索の後、私はダンディ型のプレフィックスでも作成できないという結論に達しました。 まあ、私は何ができる、私はそれを取った後、私は問題を終了します。



Arduinoとラジオエレクトロニクス全般 (広告ではない)のプロジェクト専用サイトで 、そのような取り組みに関する記事を見つけました。 プレフィックスがTV-shnayaであるため、 TVoutライブラリを使用することが決定されました。 その設置と操作のために、私は少しシャーマンをしなければなりませんでした。



必要なライブラリ関数

モード設定機能



begin()関数は、ビデオ出力を初期化します(デフォルトの画面解像度は128x96です)。

構文:

TVOut.begin(モード);

TVOut.begin(モード、x、y);

パラメータ:

モード-ビデオ信号標準:

_PAL-PALモード。

_NTSC-NTSCモード。

戻り値:

0-接続に成功した場合、4-失敗した場合(出力バッファに十分なメモリがありません)。



遅延機能



delay()関数は、出力画像を遅延させます。

構文:



TVOut.delay(ms);

パラメータ:



ms-正確な遅延(ms単位):PALでは20 ms、NTSCでは16 ms。



delay_frame()関数は、出力イメージを遅延させます。

構文:



TVOut.delay_frame(フレーム);

パラメータ:



フレーム-遅延させるフレームの数...

この機能は、画面の更新による画面のちらつきを最小限に抑えるか、解消するのに役立ちます。



パラメータ関数



hres()関数は、画面の水平解像度を返します。

構文:



TVOut.hres();

パラメータ:



いや

戻り値:



unsigned char-水平スクリーン解像度。



vres()関数は、画面の垂直解像度を返します。

構文:



TVOut.vres();

パラメータ:



いや

戻り値:



unsigned char-垂直画面解像度。



char_line()関数は、テキスト情報を表示するときに、行ごとに可能な最大文字数を返します。

構文:



TVOut。 char_line();

パラメータ:



いや

戻り値:



unsigned char-文字数。



基本的なグラフィック機能



set_pixel()関数は、指定された座標を持つポイントで画面ピクセルの色を設定します。

構文:



TVOut.set_pixel(x、y、color);

パラメータ:



x、y-ピクセル座標。

色-ピクセルの色:

0-黒;

1-白;

2-色を反転します。

get_pixel()関数は、指定された座標を持つポイントから画面ピクセルの色を取得します。

構文:



TVOut.get_pixel(x、y);

パラメータ:



x、y-ピクセル座標。

戻り値:



色-ピクセルの色:

0-黒;

1-白;

2-色を反転します。

fill()関数は、指定された色で画面を塗りつぶします。

構文:



TVOut.fill(色);

パラメータ:



色-塗りつぶし色:

0-黒;

1-白;

2-色を反転します。

clear_screen()関数は画面をクリアし、指定された色で塗りつぶします。

構文:



TVOut.clear_screen(色);

パラメータ:



色-塗りつぶし色:

0-黒;

1-白;

2-色を反転します。



invert()関数は、画面の内容を反転します。

構文:



TVOut.invert();

パラメータ:



いや

shift_direction()関数は、画面のコンテンツを移動します。

構文:



TVOut.shift_direction(距離、方向);

パラメータ:



distance-画面コンテンツをシフトする距離。

方向-シフト方向:

UP = 0-アップ;

DOWN = 1-ダウン;

LEFT = 2-左へ。

RIGHT = 3-右側。



draw_line()関数は、画面上の2点を線で接続します。

構文:



TVOut.draw_line(x0、y0、x1、y1、色);

パラメータ:



x0、y0-最初の点の座標。

x1、y1-2番目のポイントの座標。

色-塗りつぶし色:

0-黒;

1-白;

2-色を反転します。

draw_row()関数は、ラインの2つのポイント間の指定された色でラインを塗りつぶします。

構文:



TVOut.draw_row(行、x0、x1、色);

パラメータ:



row-線の垂直座標。

x1、x2-ラインポイントの水平座標。

色-塗りつぶし色:

0-黒;

1-白;

2-色を反転します。

draw_column()関数は、列の2つのポイントの間で指定された色で行を塗りつぶします。

構文:



TVOut.draw_column(列、y0、y1、色);

パラメータ:



column-列の水平座標。

y1、y2-列内のポイントの垂直座標。

色-塗りつぶし色:

0-黒;

1-白;

2-色を反転します。

draw_rect()関数は、画面上に長方形を描画します。

構文:



TVOut.draw_rect(x、y、w、h、色);

TVOut.draw_rect(x、y、w、h、color、fillcolor);

パラメータ:



x、y-左上の点の座標。

w、h-描かれた長方形の幅と高さ。

color-長方形の境界線の色:

0-黒;

1-白;

2-色を反転します。

fillcolor-長方形の塗りつぶし色:

0-黒;

1-白;

2-色を反転します。

draw_circle()関数は、画面上に円を描きます。

構文:



TVOut.draw_ circle(x、y、r、色);

TVOut.draw_ circle(x、y、r、色、塗りつぶし色);

パラメータ:



x、y-円の中心の座標。

rは円の半径です。

色-円の境界線の色:

0-黒;

1-白;

2-色を反転します。

fillcolor-円の塗りつぶし色:

0-黒;

1-白;

2-色を反転します。

ビットマップ()関数はビットマップを表示します。

構文:



TVOut.bitmap(x、y、bmp、w、h);

パラメータ:



x、y-出力ポイントの左上隅の座標。

bmp-画像が保存されているメモリ配列へのポインタ。

w、h-表示画像の幅、高さ;

以下では、出力ラスターイメージのコードを作成するプロセスを検討します。



テキスト出力関数



テキスト情報を出力する機能を使用するには、ライブラリに含まれるカスタムフォントでファイルを接続する必要があります。 カスタムフォントセットを接続するには、スケッチ内のヘッダーファイルを接続する必要があります。

#include

ライブラリには、次のフォントセットが含まれています。



font4x6;

font6x8;

font8x8;

font8x8ext。

select_font()関数は、テキスト情報を表示するためのフォントを選択します。

構文:



TVOut.select_font(フォント);

パラメータ:



font-スケッチに接続されているフォント。



print_char()関数は、画面に文字を表示します。

構文:



TVOut.print_char(x、y、char);

パラメータ:



x、y-文字を表示する画面上の位置。

charは、現在のフォントの文字です。



set_cursor()関数は、画面にテキスト情報を表示するためのカーソル位置を設定します。

構文:



TVOut.set_cursor(x、y);

パラメータ:



x、y-カーソルの座標。

print()関数は、文字列、文字、または数字を表示します。

構文:



TVOut.print(x、y、string);

TVOut.print(x、y、char、base);

TVOut.print(x、y、int、base)。

パラメータ:



x、yはカーソルの座標です。

base-出力形式:

バイト= 0;

DEC = 10(デフォルト);

HEX = 16。



println()関数は、文字列、文字、または数字を表示し、最後に改行文字を表示します:

構文:



TVOut.println(x、y、string);

TVOut.println(x、y、char、base);

TVOut.println(x、y、int、base)。

パラメータ:



x、yはカーソルの座標です。

base-出力形式:

バイト= 0;

DEC = 10(デフォルト);

HEX = 16。



オーディオ出力機能



オーディオ出力機能を使用すると、オーディオ出力を介して特定の周波数信号をテレビに送信できます。

tone()関数は、特定の周波数のオーディオ信号を出力します。

構文:



TVOut.tone(頻度、継続時間);

TVOut.tone(周波数)。

パラメータ:



frequency-オーディオ信号の周波数。

duration-信号の持続時間。

noTone()関数 、オーディオ信号の出力を停止します。

構文:



TVOut.noTone()。





ステップ2.ビデオ出力をはんだ付けする



まず、コンポジットav-out(RCA)を介してビデオ信号を出力するために特定のボードをはんだ付けする必要があります。 以下のスキームに従ってはんだ付けします。







公称値470オームと1 kオームの2つの抵抗を互いに平行に配置し、ケーブルチューリップから「プラス」をはんだ付けします。 次に、470オームの抵抗からArduinoの7番目のピンにワイヤを割り当てます。 彼はビデオ( video )の出力を担当し、同期( sync )を担当しているため、1 kオームの抵抗から9番目のピンにワイヤを接続します。 そして、ケーブルチューリップからArduinoの「地面」への「マイナス」。 詳細はこちら



ステップ3.コードを記述します(ゲーム)



接続方法については説明しません。必要な情報はいつものようにインターネット上で見つかるからです。 見つけるのが非常に難しいか、まったくないものを説明しています。



ようこそ画面から開始します 。 しかし、ここで彼は重要な質問に出会います。この奇跡とは何でしょうか? 私は頭をあきらめて、-Shimoを思いついた。 中国語ではもちろん、技術的にも高度に聞こえますが、それは問題ではありません。



次に、ゲーム自体に戻ります。 そして再び難しい質問:どんなゲームをするのか? 私はあまり勤勉で勤勉ではなく、初心者でもあるので、 ピンポンを書くことにしました。







始めます。 TV.draw_line(60,0,60,96,1)を使用して、画面の中央に線を引きます。 。 画面の中央にボールが正確に表示されます。 そのモーションvoid ballmove(int vel、int angle)の関数を書きます。 TV.set_pixel(x、y、1)を使用してインストールします。 、変数を呼び出しました。



次に、ボールを操作する前に、画面の更新を処方します。つまり、ボールが画面上で「継承」されないように、次の位置に移動するときは、前の位置を黒で塗りつぶす必要があります。 これを行うには、 TV.set_pixel(x、y、0)を登録する必要があります。 。 変数座標のすべての変更後、位置設定とわずかな遅延を登録する必要があります-TV.delay(50); 。 このようなことが起こるはずです:



void ballmove(int vel, int angle) { TV.set_pixel(x,y,0); //   TV.set_pixel(x,y,1); }
      
      





次に、座標の変更について説明します。 8方向(1〜8)、可変int角度のみ 。 回転に応じて、変数にint velocityの一部を減算または追加することは既に簡単です。 私はこれが好きでした:



  if(angle == 1) { y -= vel; } if(angle == 3) { x += vel; } if(angle == 5) { y += vel; } if(angle == 7) { x -= vel; } if(angle == 2) { x += round(vel/2); y -= round(vel/2); } if(angle == 4) { x += round(vel/2); y += round(vel/2); } if(angle == 6) { x -= round(vel/2); y += round(vel/2); } if(angle == 8) { x -= round(vel/2); y -= round(vel/2); }
      
      





これでラケットが動きます。 ここに重要な説明があります-xに沿ったラケットの位置は変わらないため、 yに沿った座標のみを使用しました。 次の関数void racketsmove()を記述します。 次に、ラケット、変数int yb1int yb2TV.draw_line(10、yb1 + 8、10、yb1-8、1)を描画します。 およびTV.draw_line(110、yb2 + 8、110、yb2-8、1); 。 ボールの場合と同様に、画面の更新、つまり「トレースなし」。



ラケットの管理はボタンで行われます。 ボタン、ピン23-最初のラケット、 4と5-2番目のラケットを接続します。 キーストロークを確認し、座標を変更します。



関数は次のとおりです。



 void racketsmove() { TV.draw_line(10, yb1+8, 10, yb1-8, 0); TV.draw_line(110, yb2+8, 110, yb2-8, 0); if((yb1 - 8) > 1) { if(digitalRead(2) == HIGH) { yb1 -= 2;} } if((yb1 + 8) < 95) { if(digitalRead(3) == HIGH) {yb1 += 2;} } if((yb2 - 8) > 1) { if(digitalRead(4) == HIGH) {yb2 -= 2; } } if((yb2 + 8) < 95) { if(digitalRead(5) == HIGH) {yb2 += 2;} } TV.draw_line(10, yb1+8, 10, yb1-8, 1); TV.draw_line(110, yb2+8, 110, yb2-8, 1); }
      
      





さて、 ボールに戻りましょう。 次に、壁とラケットからの衝突と反発を書きましょう。 関数-void ballcol() 。 これを行うには、オブジェクトに対する相対位置を確認してから、角度を確認します。 次に、この角度を別の角度に変更します。 角度があれば、推測するのは簡単です。
反射角は入射角に等しい
ラケットの特定の領域について、いくつかの物理的な例外を作成できます。



機能:



 void ballcol() { if(x == 1 || x == 119 || (x == 10 && y < (yb1 + 3) && y > (yb1 - 3)) || (x == 110 && y < (yb2 + 3) && y > (yb2 - 3))) { if(a==1){a=5;}else if(a==2){a=8;}else if(a==3){a=7;}else if(a==4){a=6;}else if(a==5){a=1;}else if(a==6){a=4;}else if(a==7){a=3;}else if(a==8){a=2;} } if(x == 10 && y < (yb1 - 3) && y > (yb1 - 8)) { a = 2; } if(x == 10 && y > (yb1 + 3) && y < (yb1 + 8)) { a = 4; } if(x == 110 && y < (yb2 - 3) && y > (yb2 - 8)) { a = 8; } if(x == 110 && y > (yb2 + 3) && y < (yb2 + 8)) { a = 6; } if(y == 95 || y == 1) { if(a==1){a=5;}else if(a==2){a=4;}else if(a==3){a=7;}else if(a==4){a=2;}else if(a==5){a=1;}else if(a==6){a=8;}else if(a==7){a=3;}else if(a==8){a=6;} } }
      
      





背後にある最も難しい部分は、あなたが正常に呼吸することができます。



現時点では、スコアリングシステム、タイマー、および再起動しかできません。



タイマーから始めましょう。 秒float tsの変数(絶対に常に格納されます)、変数int tmtsから取得する分数)があります。 tmの値は tm = ts / 60の操作で設定します 。 そして、 TV.print(81,1、tm)画面に値を表示します TV.print(97.1、 "。"); TV.print(100,1、int(ts-(tm * 60)));



続けましょう。 再起動関数は、 void restart()を呼び出します 。 ここで、変数の初期値を返します。



コード:



 void restart() { TV.clear_screen(); x = 60; y = 48; yb1 = 48; yb2 = 48; a = 8; ts = 900.0; c1 = 0; c2 = 0; }
      
      





最終的な採点システムは、単純すぎます。 Googleを開き、「卓球ゲームのルール」に進みます。 どんなポイントが与えられるかを探しています。 罰金に関する部分を見つけると、次のことがわかりました。「最初のリバウンド後に相手に送られたボールを正しく反映する時間がない場合、ポイントが勝ったとみなされます。」 問題は、熟成、ストロークやものを数える方法ですか?..そして、2次元グラフィックスのピンポンのため、ストロークを数える必要はありません。



私たちは落ち着いて道を見つけ、いつものように、ちょうど側壁に対する座標をチェックします。 衝突が発生した場合、フィールドの反対側にいるプレーヤーにポイントが加算されます。 関数-void ballscount() 。 タイマーが出たら、最初のプレーヤー( int c1変数)と2番目のプレーヤー( int c2変数)のスコアを比較し、勝者を宣言し、遅延を加えて再起動します。



コード:



 void ballscount() { if(x == 1) { c2++; } if(x == 119) { c1++; } if(c1 > c2 && ts == 0) { TV.println(10, 45, "Player 1 won!"); delay(10000); restart(); } else if(c1 < c2 && ts == 0) { TV.println(10, 45, "Player 2 won!"); delay(10000); restart(); } else if(c1 == c2 && ts == 0) { TV.println(10, 45, "You are equal"); delay(10000); restart(); }
      
      





これですべてです、ゲームコードを完全に作成しました。 かなり面白くて、プレイできます。







怠け者のために、私はコード全体を書くだけです。



完全なスクリプト
合計218行。

 #include <TVout.h> #include <fontALL.h> TVout TV; int x, y, a, c1, c2, yb1, yb2, tm, tsh, s; float ts; boolean paused = false; void setup ( ) { TV.begin(NTSC, 120, 96); TV.clear_screen(); TV.select_font(font6x8); TV.println( 0, 50, "Welcome to Shimo" ); TV.delay (5000); TV.clear_screen(); x = 60; y = 48; yb1 = 48; yb2 = 48; a = 8; ts = 900.0; s = 2; } void loop ( ) { if(!paused) { TV.draw_line(60,0,60,96,1); TV.select_font(font8x8); racketsmove(); ballscount(); TV.print(1,1,c1); TV.print(18,1,":"); TV.print(26,1,c2); tm = ts / 60; ts -= 0.04; if(ts < 0) { ts = 0; } TV.draw_rect(81,1,38,10,0,0); TV.print(81,1,tm); TV.print(97,1,"."); TV.print(100,1,int(ts-(tm*60))); ballcol(); /*if(ts < 600) { s = 4; } if(ts < 300) { s = 6; }*/ ballmove(s, a); TV.delay(50); if(digitalRead(6) == HIGH) { paused = true; delay(1000); } } else { TV.println(40,4,"pause"); if(digitalRead(6) == HIGH) { paused = false; delay(1000); TV.clear_screen(); } } } void ballscount() { if(x == 1) { c2++; } if(x == 119) { c1++; } if(c1 > c2 && ts == 0) { TV.println(10, 45, "Player 1 won!"); delay(10000); restart(); } else if(c1 < c2 && ts == 0) { TV.println(10, 45, "Player 2 won!"); delay(10000); restart(); } else if(c1 == c2 && ts == 0) { TV.println(10, 45, "You are equal"); delay(10000); restart(); } } void ballcol() { if(x == 1 || x == 119 || (x == 10 && y < (yb1 + 3) && y > (yb1 - 3)) || (x == 110 && y < (yb2 + 3) && y > (yb2 - 3))) { if(a==1){a=5;}else if(a==2){a=8;}else if(a==3){a=7;}else if(a==4){a=6;}else if(a==5){a=1;}else if(a==6){a=4;}else if(a==7){a=3;}else if(a==8){a=2;} } if(x == 10 && y < (yb1 - 3) && y > (yb1 - 8)) { a = 2; } if(x == 10 && y > (yb1 + 3) && y < (yb1 + 8)) { a = 4; } if(x == 110 && y < (yb2 - 3) && y > (yb2 - 8)) { a = 8; } if(x == 110 && y > (yb2 + 3) && y < (yb2 + 8)) { a = 6; } if(y == 95 || y == 1) { if(a==1){a=5;}else if(a==2){a=4;}else if(a==3){a=7;}else if(a==4){a=2;}else if(a==5){a=1;}else if(a==6){a=8;}else if(a==7){a=3;}else if(a==8){a=6;} } } void racketsmove() { TV.draw_line(10, yb1+8, 10, yb1-8, 0); TV.draw_line(110, yb2+8, 110, yb2-8, 0); if((yb1 - 8) > 1) { if(digitalRead(2) == HIGH) { yb1 -= 2; } } if((yb1 + 8) < 95) { if(digitalRead(3) == HIGH) { yb1 += 2; } } if((yb2 - 8) > 1) { if(digitalRead(4) == HIGH) { yb2 -= 2; } } if((yb2 + 8) < 95) { if(digitalRead(5) == HIGH) { yb2 += 2; } } TV.draw_line(10, yb1+8, 10, yb1-8, 1); TV.draw_line(110, yb2+8, 110, yb2-8, 1); } void ballmove(int vel, int angle) { TV.set_pixel(x,y,0); if(angle == 1) { y -= vel; } if(angle == 3) { x += vel; } if(angle == 5) { y += vel; } if(angle == 7) { x -= vel; } if(angle == 2) { x += round(vel/2); y -= round(vel/2); } if(angle == 4) { x += round(vel/2); y += round(vel/2); } if(angle == 6) { x -= round(vel/2); y += round(vel/2); } if(angle == 8) { x -= round(vel/2); y -= round(vel/2); } TV.set_pixel(x,y,1); } void restart() { TV.clear_screen(); x = 60; y = 48; yb1 = 48; yb2 = 48; a = 8; ts = 900.0; c1 = 0; c2 = 0; }
      
      







ステップ4.体を切り取る



私は、4mmの合板からレーザーカッター(またはフライス盤、確かにわかりません)でケースを切ることにしました。 私はInkScapeで描き、少し恥ずかしがり、フライス盤形式に変換しました。







ゲームパッドについては、小さなタブレットを切り取り、ボタン用の穴を開けました。 かなりうまくいきましたが、残念ながら写真を失いました。



おわりに



その過程で、Arduinoに2つのゲームパッドを備えた標準のピンポンゲームを搭載したシンプルなゲームテレビゲームコンソールを作成しました。



追加のソースとメモ





1. ライブラリに関する情報

2. 接続ポートに関する情報

3. 一部のリクエストでは、カバー画像が圧縮されていました。



All Articles