// 『昼夜逆転』工作室 http://jsdiy.web.fc2.com/ // ATtiny2313 // 7セグ4桁ボード // // セグメントアタック // // 2008/10/12 v1.00 初版 /* 【ゲーム内容】 壊れた数字にセグメント(数字のかけら)を投げつけて倒すゲーム。 【ゲームの始め方】 電源オンでタイトル画面「S.ATK」。 スイッチA0でゲーム開始。 スイッチA1でハイスコア画面へ。 【ゲームの操作方法】 1桁目が投げるかけら、4桁目が壊れている数字。 スイッチA0: かけらを投げる。 スイッチA1: ゲーム中は使用せず。 【ゲームの遊び方】 プレイヤー(1桁目)が投げるかけらがランダムで表示される。 投げたかけらが壊れた数字にはまって、数字が完成したら1個クリア(倒した)。 完成すると目印として「.」が付いた後、数字ごと消え、次の壊れた数字が現れる。 既に数字の一部があるところへ投げたら、その位置のかけらは消える。 一定時間が経過するとごとに壊れた数字はプレイヤー側へ移動してくる。 プレイヤーに重なったらゲームオーバー、スイッチA0/A1でスコア画面へ。 スコア画面からスイッチA0/A1でハイスコア画面へ。 ハイスコア画面からスイッチA0/A1でタイトル画面へ。 */ /* ポート対応 [ATtiny2313] bit 7 6 5 4 3 2 1 0 PORTA - - - - - - swL swR PORTB - - - DP 桁4 桁3 桁2 桁1 LEDボードでは左から桁1-桁4 PORTD - G F E D C B A 7セグのセグメント(下図) 7セグのセグメント図 +-A-+ F B +-G-+ E C +-D-+ DP */ #define _CATHODE_CMN_ 0 #if _CATHODE_CMN_ #define SET_PORTB(n) PORTB = (n) #define SET_PORTD(n) PORTD = (n) #else #define SET_PORTB(n) PORTB = (~(n) & 0b00011111) #define SET_PORTD(n) PORTD = (~(n) & 0b01111111) #endif #include #include //#include //これを使わず自力組み込みrand()にすると約300byte減らせる const uint8_t SEGCHAR[] = { // 0b0GFEDCBA 0b00111111, //0 0b00000110, //1 0b01011011, //2 0b01001111, //3 0b01100110, //4 0b01101101, //5 0b01111101, //6 0b00100111, //7 0b01111111, //8 0b01101111, //9 0b00000000, //null // 0b0GFEDCBA 0b01101101, //S 「5」と同じ形だが区別する 0b01110111, //A 0b00110001, //T 0b01110101, //K 0b01110011, //P 0b01110110 //H }; enum _SIDX { SIDX_NULL = 10, SIDX_S, SIDX_A, SIDX_T, SIDX_K, SIDX_P, SIDX_H }; #define SEG_DP 0b00010000 enum _STATUS { ST_TITLE, ST_INIT, ST_PLAY, ST_HIT, ST_OVER, ST_SCORE, ST_HISCORE }; #define PLAYER_LIFETIME_DEFAULT (1500 / 250) //1.5秒(250ms単位) #define PLAYER_LIFETIME_FASTEST (1000 / 250) //1.0秒(250ms単位) #define ENEMY_MOVEWAIT_DEFAULT (PLAYER_LIFETIME_DEFAULT * 7) #define SCORE_MAX 999 //よく知られているC言語標準関数の乱数------------------------- #define RAND_MAX 32767 static unsigned long _rand_next = 1; void srand(unsigned int seed) { _rand_next = seed; } int rand(void) { _rand_next = _rand_next * 1103515245 + 12345; return (unsigned int)(_rand_next / 65536) % (RAND_MAX + 1); } //---------------------------------------------------------- volatile uint8_t tci; //8bitタイマ/カウンタ割り込み ISR(TIMER0_COMPA_vect) { tci = 1; } uint8_t led[4] = {0}; uint8_t segDP[4] = {0}; void setScoreLED(uint16_t score, uint8_t sidx) { uint16_t tmp = score; uint8_t i; led[0] = SEGCHAR[sidx]; led[1] = SEGCHAR[tmp / 100]; tmp %= 100; led[2] = SEGCHAR[tmp / 10]; tmp %= 10; led[3] = SEGCHAR[tmp]; for (i = 1; i < 3 && led[i] == SEGCHAR[0]; i++) led[i] = 0; //ゼロサプレス segDP[0] = SEG_DP; segDP[1] = segDP[2] = segDP[3] = 0; } int main(void) { uint8_t k, state, i; uint8_t sw, psw, loopCnt; uint8_t swOnA[2] = {0}; //スイッチの入力状態 1:on 0:off uint8_t player, createPlayer, playerLifeTime, prevPlayer; uint8_t enemy, createEnemy, enemyPos, enemyMoveWait; uint8_t hitWaitCnt; uint16_t hitCount; uint8_t nowPlayerLifetime, nowEnemyMoveWait; uint16_t score, hiscore, tmp; //ポート設定 DDRD = 0b01111111; //7セグLEDのA-G = PD0-PD6 DDRB = 0b00011111; //7セグLEDの桁位置(LEDボードの左から) = PB0-PB3, 7セグLEDのDP = PB4 PORTA = 0b00000011; //スイッチが2個(制御ボードの右から) PA0-PA1 //8bitタイマ設定 TCCR0A = 0b00000010; //CTC動作 TCCR0B = 0b00000101; //カウント周期は 1MHz/1024 = 約1kHz → 約1ms間隔 OCR0A = 250; //割り込み周期は 1ms * 250 = 250ms TIMSK = 0b00000001; //比較一致割り込み state = ST_TITLE; k = 0; sw = psw = 0; loopCnt = 0; player = createPlayer = playerLifeTime = prevPlayer = 0; enemy = createEnemy = enemyPos = enemyMoveWait = 0; hitWaitCnt = 0; hitCount = 0; nowPlayerLifetime = PLAYER_LIFETIME_DEFAULT; nowEnemyMoveWait = ENEMY_MOVEWAIT_DEFAULT; score = hiscore = 0; while (1) { //スイッチ読み取り swOnA[0] = swOnA[1] = 0; if ((++loopCnt & 0x40) == 0x40) //チャタリングが回避できる程度にループ間隔を空ける { sw = PINA; if (psw != sw) { swOnA[0] = ~sw & 0b00000001; swOnA[1] = ~sw & 0b00000010; psw = sw; } } //動作状態 switch (state) { case ST_INIT: for (i = 0; i < 4; i++, led[i] = 0, segDP[i] = 0); player = 0; createPlayer = 1; createEnemy = 1; hitCount = 0; nowPlayerLifetime = PLAYER_LIFETIME_DEFAULT; nowEnemyMoveWait = ENEMY_MOVEWAIT_DEFAULT; score = 0; tci = 0; sei(); //割り込み許可 state = ST_PLAY; break; case ST_PLAY: //プレイヤーの弾を生成する if (createPlayer) { createPlayer = 0; do { player = 1 << (rand() % 7); } while (prevPlayer == player); //前回の形と同じにならないようにする prevPlayer = player; //今回生成した形を保存しておく playerLifeTime = 0; led[0] = player; } //敵を生成する if (createEnemy) { createEnemy = 0; enemy = 0; do{ //空白の敵にならないようにする for (i = 0; i < 7; i++) enemy |= ((rand() % 100 < 30 ? 1 : 0) << i); } while (enemy == 0); enemyPos = 3; //敵の出現位置(4桁目) enemyMoveWait = 0; led[enemyPos] = enemy; } //発射 if (swOnA[0]) { uint8_t match; //敵にセグメントをはめる、取る if (enemy & player) enemy &= ~player; else enemy |= player; led[enemyPos] = enemy; //この弾を無効にする(表示を消す) player = 0; led[0] = 0; //数字形成を判定する for (match = 0, i = 0; i < 10 && !match; i++) if (enemy == SEGCHAR[i]) match = 1; if (match) //敵を倒した { uint8_t bits, bitCnt; //スコア計算 for (bits = enemy, bitCnt = 0, i = 0; i < 7; i++, bits >>= 1) bitCnt += (bits & 1); //セグメントの個数をカウントする score += bitCnt + enemyPos; if (SCORE_MAX < score) score = SCORE_MAX; //スピード調節 if (++hitCount % 10 == 0) { if (PLAYER_LIFETIME_FASTEST < nowPlayerLifetime) { nowPlayerLifetime--; nowEnemyMoveWait = nowPlayerLifetime * 7; } } segDP[enemyPos] = SEG_DP; hitWaitCnt = 0; state = ST_HIT; break; } //数字を作らず、敢えてセグメントを全消ししたらボーナス点獲得 if (enemy == 0) score += 20; } if (tci) //タイマ/カウンタ割り込み発生 { tci = 0; //一定時間が経過したらプレイヤーの弾を変更する if (++playerLifeTime == nowPlayerLifetime) createPlayer = 1; //一定時間が経過したら敵を移動する if (++enemyMoveWait == nowEnemyMoveWait) { enemyMoveWait = 0; led[enemyPos] = 0; enemyPos--; led[enemyPos] = enemy; } if (enemyPos == 0) //プレイヤーと衝突 { if (hiscore < score) hiscore = score; state = ST_OVER; break; } } break; case ST_HIT: if (tci) { tci = 0; if (++hitWaitCnt == 4) //250ms単位で1sec待つ(1sec/250ms=4カウント) { segDP[enemyPos] = 0; led[enemyPos] = 0; createPlayer = 1; createEnemy = 1; state = ST_PLAY; } } break; case ST_OVER: if (tci) { tci = 0; segDP[0] = SEG_DP - segDP[0]; } if (swOnA[0] || swOnA[1]) { cli(); //割り込み禁止 setScoreLED(score, SIDX_P); state = ST_SCORE; } break; case ST_SCORE: if (swOnA[0] || swOnA[1]) { setScoreLED(hiscore, SIDX_H); state = ST_HISCORE; } break; case ST_HISCORE: if (swOnA[0] || swOnA[1]) state = ST_TITLE; break; default: //ST_TITLE led[0] = SEGCHAR[SIDX_S]; led[1] = SEGCHAR[SIDX_A]; led[2] = SEGCHAR[SIDX_T]; led[3] = SEGCHAR[SIDX_K]; segDP[0] = SEG_DP; segDP[1] = segDP[2] = segDP[3] = 0; if (swOnA[0]) { srand(loopCnt); state = ST_INIT; } if (swOnA[1]) { setScoreLED(hiscore, SIDX_H); state = ST_HISCORE; } break; } //表示 SET_PORTB(0); SET_PORTD(0); SET_PORTB((1<< k) | segDP[k]); SET_PORTD(led[k]); k = (k + 1) % 4; } return 0; }