遊戲技術相關研究

2017年2月12日 星期日

【Arduino】自己的掌機自己作(1)-線路圖與開發套件

上午7:19 Posted by Channel Chung 5 comments
     年終打掃的時候在家掃出了一堆之前在網路上亂買的Arduino零件,想說丟了又浪費所以就索性兜一兜來弄點東西玩玩,看了 一下手上的零件忽然想到可以實作一台簡單的8x8螢幕遊戲機,所以就立馬動手規劃線路圖了,完成後的線路圖如下:


依照線路圖時實際接線後的樣貌如下:

  
接下來就是開始填寫8x8遊戲機程式開發套件了,依照我們用到的硬體零件有螢幕(KT點矩陣MAX7219)、搖桿(Joy Stick XY)、蜂鳴器,所以我們要分別為這些硬體填寫控制程式,各程式功能說明如下:

➤處理控制螢幕(KT點矩陣MAX7219)繪圖相關函數
Game8x8Graphics.h
#ifndef _GAME8X8GRAPHICS_h
#define _GAME8X8GRAPHICS_h

#if defined(ARDUINO) && ARDUINO >= 100
 #include "arduino.h"
#else
 #include "WProgram.h"
#endif

#include <Arduino.h>
#include "LedControl.h"

class Game8x8Graphics {
private:
 // 畫布.
 byte canvas[8][8] = {
  { 0,0,0,0,0,0,0,0 },
  { 0,0,0,0,0,0,0,0 },
  { 0,0,0,0,0,0,0,0 },
  { 0,0,0,0,0,0,0,0 },
  { 0,0,0,0,0,0,0,0 },
  { 0,0,0,0,0,0,0,0 },
  { 0,0,0,0,0,0,0,0 },
  { 0,0,0,0,0,0,0,0 },
 };

public:
 // 控制MAX7219函數庫.
 LedControl *lc;
 // 建構式.
 Game8x8Graphics();
 // 更新.
 void update();
 // 清除.
 void clearDisplay(byte state);
 // 設定像素.
 void setPixcls(int row, int col, byte state);
};
Game8x8Graphics.cpp
#include "Game8x8Graphics.h"

// 建構式.
Game8x8Graphics::Game8x8Graphics() {
 // 建立MAX7219函數庫.
 lc = new LedControl( 12, 11, 10, 1);
 // 矩陣LED相關.
 lc->shutdown(0, false);
 // 設定LED亮度.
 lc->setIntensity(0, 0);
 // 清除顯示.
 //lc->clearDisplay(0);  
}

// 更新.
void Game8x8Graphics::update() {
 // 更新畫面.
 for (int x = 0; x<8; x++) {
  for (int y = 0; y<8; y++) {
   if (canvas[x][y] == 1) {
    lc->setLed(0, x, y, true);
   }
   else {
    lc->setLed(0, x, y, false);
   }
  }
 }
}

// 清除.
//  state:
//   0:全黑.
//   1:全亮.
void Game8x8Graphics::clearDisplay(byte state) {
 for (int x = 0; x<8; x++) {
  for (int y = 0; y<8; y++) {
   canvas[x][y] = state;
  }
 }
}

// 設定像素.
//  row:x.
//  col:y.
//  state:
//   0:全黑.
//   1:全亮.
void Game8x8Graphics::setPixcls(int x, int y, byte state) {
 if (x>7) { x = 7; }if (x<0) { x = 0; }
 if (y>7) { y = 7; }if (y<0) { y = 0; }
 canvas[y][x] = state;
}

➤處理取得搖桿(Joy Stick XY)訊息相關函數
Game8x8JoyStick.h
#ifndef _GAME8X8JOYSTICK_h
#define _GAME8X8JOYSTICK_h

#if defined(ARDUINO) && ARDUINO >= 100
 #include "arduino.h"
#else
 #include "WProgram.h"
#endif

#include <Arduino.h>

class Game8x8JoyStick
{
private:

 // 取得數值.
 int value = 0;

public:
 // 建構式.
 Game8x8JoyStick();
 // x.
 int getJoyX();
 // y.
 int getJoyY();
 // z.
 int getJoyZ();
};
#endif
Game8x8JoyStick.cpp
#include "Game8x8JoyStick.h"

// 建構式.
Game8x8JoyStick::Game8x8JoyStick() {
 pinMode( 7, OUTPUT);
}

// x.
// ->:5~0
// <-:5~10
int Game8x8JoyStick::getJoyX() {
 value = analogRead(0);
 value /= 100;
 return value;
}

// y.
// ^:5~0
// v:5~10
int Game8x8JoyStick::getJoyY() {
 value = analogRead(1);
 value /= 100;
 return value;
}

// z.
// 0:放開.
// 1:按下.
int Game8x8JoyStick::getJoyZ() {
 value = digitalRead(7);
 return value;
}

➤處理控制蜂鳴器發生相關函數
Game8x8Tone.h 
#ifndef _GAME8X8TONE_h
#define _GAME8X8TONE_h

#if defined(ARDUINO) && ARDUINO >= 100
 #include "arduino.h"
#else
 #include "WProgram.h"
#endif

#include <Arduino.h>

class Game8x8Tone
{
private:
 // pin編號.
 const static byte SP_PIN = 9;

public:
 // 建構式.
 Game8x8Tone();
 // 播放音效.
 void playTone( unsigned int frequency, unsigned long duration = 0);
};

#endif
Game8x8Tone.cpp
#include "Game8x8Tone.h"

// 建構式.
Game8x8Tone::Game8x8Tone() {
 pinMode( SP_PIN, OUTPUT);
}

// 播放音效.
void Game8x8Tone::playTone( unsigned int frequency, unsigned long duration = 0) {
 tone( SP_PIN, frequency, duration);
}

處理遊戲用時脈系統
Game8x8Time.h
#ifndef _GAME8X8TIME_h
#define _GAME8X8TIME_h

#if defined(ARDUINO) && ARDUINO >= 100
 #include "arduino.h"
#else
 #include "WProgram.h"
#endif

#include <Arduino.h>

class Game8x8Time
{
 private:
  // 相差時間.
  unsigned long timeTemp = 0;
  // 相差時間累積.
  unsigned long timeTick = 0;

 public:
  // 建構式.
  Game8x8Time();
  // 更新.
  bool update(unsigned long tick, bool cls = true);
};
#endif
Game8x8Time.cpp
#include "Game8x8Time.h"

// 建構式.
Game8x8Time::Game8x8Time() {
}

// 更新.
// tick:啟動的時間.
//  cls:每次啟動後清除時脈.
bool Game8x8Time::update(unsigned long tick, bool cls = true) {  
 timeTick += millis() - timeTemp;
 timeTemp = millis();

 if (timeTick >= tick) {
  timeTick -= tick;
  if (cls) { timeTick = 0; }
  return true;
 }
 else {
  return false;
 }
}

以上就是簡易的8x8遊戲機程式開發套件,再來就是要撰寫測試程式來檢驗一下開發套件功能,測試程式內容我們就來實作讓搖桿上下左右去控制螢幕上的一個紅點移動,同時在移動的時候還會發出音效,程式碼如下:
Game8x8_test.ino
#include "Game8x8Tone.h"
#include "Game8x8JoyStick.h"
#include "Game8x8Time.h"
#include "Game8x8Graphics.h"

// 繪圖函數.
Game8x8Graphics game8x8Graphics = Game8x8Graphics();
// 搖桿函數.
Game8x8JoyStick game8x8JoyStick = Game8x8JoyStick();
// 蜂鳴器函數.
Game8x8Tone  game8x8Tone = Game8x8Tone();

// 繪圖更新時脈.
Game8x8Time TimeGraphicsUpdate = Game8x8Time();
// 搖桿更新時脈.
Game8x8Time TimeMoveX = Game8x8Time();
Game8x8Time TimeMoveY = Game8x8Time();

// 初始位置.
int x = 4;
int y = 4;

// 初始.
void setup()
{  
 Serial.begin(9600);

 // 清除畫面.
 game8x8Graphics.clearDisplay(0);

 // 更新.
 game8x8Graphics.setPixcls( x, y, 1);
 game8x8Graphics.update();
}

// 主迴圈.
void loop() {

 // *移動搖桿X.
 int mx = game8x8JoyStick.getJoyX();
 if (mx  != 5) {
  // 右移.
  if (mx < 5) {
   if (mx < 1) { mx = 1; }
   if (TimeMoveX.update( mx * 40)) {
    x++;
    if (x > 7) { x = 7; }
    game8x8Graphics.clearDisplay(0);
    game8x8Graphics.setPixcls(x, y, 1);
   }
   // 發出聲音.
   game8x8Tone.playTone( 262, 33);
  }
  // 左移.
  else 
  {
   if (mx > 9) { mx = 10; }
   int mxT = (5 - (mx - 5));
   if (mxT == 0) { mxT = 1; }
   if (TimeMoveX.update(mxT  * 40)) {
    x--;
    if (x < 1) { x = 0; }
    game8x8Graphics.clearDisplay(0);
    game8x8Graphics.setPixcls(x, y, 1);
   }
   // 發出聲音.
   game8x8Tone.playTone( 294, 33);
  }
 }

 // *移動搖桿Y.
 int my = game8x8JoyStick.getJoyY();
 if (my != 5) {
  // 右移.
  if (my < 5) {
   if (my < 1) { my = 1; }
   if (TimeMoveY.update(my * 40)) {
    y++;
    if (y > 7) { y = 7; }
    game8x8Graphics.clearDisplay(0);
    game8x8Graphics.setPixcls(x, y, 1);
   }
   // 發出聲音.
   game8x8Tone.playTone( 330, 33);
  }
  // 左移.
  else
  {
   if (my > 9) { my = 10; }
   int myT = (5 - (my - 5));
   if (myT == 0) { myT = 1; }
   if (TimeMoveY.update(myT * 40)) {
    y--;
    if (y < 1) { y = 0; }
    game8x8Graphics.clearDisplay(0);
    game8x8Graphics.setPixcls(x, y, 1);
   }
   // 發出聲音.
   game8x8Tone.playTone( 349, 33);
  }
  
 }
 
 // 繪圖更新時脈-FPS 30.
 if (TimeGraphicsUpdate.update(33)) {
  // 更新.
  game8x8Graphics.update();
 }

 // 設定1/1000秒.
 delay(1);
}
以上,現在可以將程式上傳到Arduino版子上了,如果沒有意外的話就可以在螢幕看到一點紅點,移動搖桿可以控制紅點移動並發出音效,下面是實際操作的影片:

 

完成,下次有空試著寫些小遊戲來玩玩好了。


5 則留言:

  1. 版主您好:
    想請問一下有library嗎

    回覆刪除
    回覆
    1. Hi 張煊您好...
      有的..所有原始碼都在GitHub上囉...歡迎下載使用..
      https://github.com/channel2007/Game8x8_MovePoint

      刪除
  2. 請問版住,上傳時以下發生編譯錯誤,要如何修正?

    C:\Users\user\Documents\Arduino\libraries\Game8x8_MovePoint-master\Game8x8Time.cpp:24:61: error: default argument given for parameter 2 of 'bool Game8x8Time::update(long unsigned int, bool)' [-fpermissive]
    bool Game8x8Time::update(unsigned long tick, bool cls = true) {
    ^
    In file included from C:\Users\user\Documents\Arduino\libraries\Game8x8_MovePoint-master\Game8x8Time.cpp:11:0:
    C:\Users\user\Documents\Arduino\libraries\Game8x8_MovePoint-master\Game8x8Time.h:35:8: error: after previous specification in 'bool Game8x8Time::update(long unsigned int, bool)' [-fpermissive]
    bool update(unsigned long tick, bool cls = true);

    回覆刪除
    回覆
    1. hi 銘杉:
      請開以下這個檔案編譯後上傳看看..
      Game8x8_MovePoint.ino

      刪除
  3. 有試過囉...
    下載完的資料夾直接丟到library沒錯吧?

    回覆刪除