KT0913とCH32V003でDSPラジオ


まずは、1局指定でラジオが聞けました。
電源オンの後、チップ起動時にI2Cのコマンドが飛んでしまうので、タイミング取りました。

【スケッチ】

#include <Wire.h>

#define KT0913_ADDR 0x35

void writeRegister(uint8_t reg, uint16_t value) {
  Wire.beginTransmission(KT0913_ADDR);
  Wire.write(reg);
  Wire.write((uint8_t)(value >> 8));
  Wire.write((uint8_t)(value & 0xFF));
  Wire.endTransmission();
}

void initRadio(float freq_mhz) {
  // 1. ラジオICの電源・水晶安定待ち
  delay(500);

  // 2. ミュート解散 (Reg 0x02)
  writeRegister(0x02, 0x0007); // 100kHzステップ、左右ミュートOFF[cite: 1]
  delay(100);

  // 3. 周波数計算 & チューニング実行 (Reg 0x03)
  uint16_t chan = (uint16_t)(freq_mhz * 20.0);
  uint16_t tune_cmd = chan | 0x8000; // Bit 15 (FMTUNE) = 1[cite: 1]
  
  writeRegister(0x03, tune_cmd);
  delay(200);
}

void setup() {
  Wire.begin();
  
  // 電源ON時に自動で 81.3MHz を選局して鳴らす
  initRadio(81.3);
}

void loop() {
  // ループ内は空で問題ありません
  // 電源を切らない限り受信を継続します
}

I2Cスキャンツール(CH32V003)

Arduino フレームワークのソースです。

#include <Wire.h>

void setup() {
  Serial.begin(115200);
  delay(1000);
  
  // CH32V003の標準I2Cピンを初期化
  Wire.begin();
  Serial.println("\n=== I2C Scanner Start ===");
}

void loop() {
  byte error, address;
  int nDevices = 0;

  for (address = 1; address < 127; address++) {
    Wire.beginTransmission(address);
    error = Wire.endTransmission();

    if (error == 0) {
      Serial.print("I2C device found at address 0x");
      if (address < 16) Serial.print("0");
      Serial.print(address, HEX);
      Serial.println(" !");
      nDevices++;
    }
  }

  if (nDevices == 0) {
    Serial.println("No I2C devices found\n");
  } else {
    Serial.println("Scan done\n");
  }

  delay(3000);
}

◆I2C I/Oバスエクスパンダー経由のLチカ


接続は下記記事を参照しました。
burariweb.info

【Arduino UNO版 Arduino IDEのソースコード】

#include <Wire.h>                  // ライブラリのインクルード
#include <Adafruit_MCP23X17.h>
Adafruit_MCP23X17 mcp;             // MCPライブラリのオブジェクトを作成

void setup() {
  
  Wire.begin();
  mcp.begin_I2C();                     // MCP23017のI2Cアドレスを0x00に設定(0x00は省略可)

  for (int i = 8; i <= 15; i++) {  // 8~15(GPB0~GPB7)を出力に設定
    mcp.pinMode(i, OUTPUT);
  }

}

void loop() {

  for (int i = 8; i <= 15; i++) {  // 8~15(GPB0~GPB7)に接続されたLEDを点灯
    mcp.digitalWrite(i, HIGH);
    delay(30);
  }

  for (int i = 8; i <= 15; i++) {  // 8~15(GPB0~GPB7)に接続されたLEDを消灯
    mcp.digitalWrite(i, LOW);
    delay(30);
  }
}

【CH32V003版 Arduino IDEのソースコード】

#include <Wire.h>

// MCP23017のI2Cアドレス(A0~A2がGNDなら0x20)
#define MCP_ADDR 0x20 

// MCP23017内部レジスタアドレス
#define IODIRB  0x01  // Port B 方向設定レジスタ (0:出力, 1:入力)
#define GPIOB   0x13  // Port B 出力レジスタ

// I2Cレジスタ書き込み用ヘルパー関数
void writeMCP(uint8_t reg, uint8_t value) {
  Wire.beginTransmission(MCP_ADDR);
  Wire.write(reg);
  Wire.write(value);
  Wire.endTransmission();
}

void setup() {
  // CH32V003のデフォルトI2Cピン(PC1:SDA, PC2:SCL)を初期化
  Wire.begin(); 
  
  // GPB0~GPB7 (Port B) をすべて出力モード (0x00) に設定
  writeMCP(IODIRB, 0x00);
  
  // 初期状態として全点滅オフ (0x00)
  writeMCP(GPIOB, 0x00);
}

void loop() {
  uint8_t outputPattern = 0x00;

  // 1ピンずつONにしていく
  for (int i = 0; i < 8; i++) {
    outputPattern |= (1 << i); // ビットを立たせる
    writeMCP(GPIOB, outputPattern);
    delay(30);
  }

  // 1ピンずつOFFにしていく
  for (int i = 0; i < 8; i++) {
    outputPattern &= ~(1 << i); // ビットを落とす
    writeMCP(GPIOB, outputPattern);
    delay(30);
  }
}

ちょっと変わったマンデルブロセット

【ソース】

#include <stdio.h>

// 描画するアスキーアートのサイズ(モニタの幅に合わせて調整してください)
#define WIDTH  80
#define HEIGHT 40
#define MAX_ITER 30

int main(void) {
    // 画面をクリアしてホームポジションに移動するエスケープシーケンス
    printf("\033[2J\033[H");
    printf("--- Mandelbrot Set ---\r\n");

    for (int y = 0; y < HEIGHT; y++) {
        // 複素数平面の虚部 (Im) へマッピング (-1.2 から 1.2)
        float ci = -1.2f + (2.4f * y / HEIGHT);

        for (int x = 0; x < WIDTH; x++) {
            // 複素数平面の実部 (Re) へマッピング (-2.0 から 0.6)
            float cr = -2.0f + (2.6f * x / WIDTH);

            float zr = 0.0f;
            float zi = 0.0f;
            int iter = 0;

            // 漸化式 Z_{n+1} = Z_n^2 + C の計算
            // |Z| > 2 (つまり zr^2 + zi^2 > 4) になるまでループ
            while (zr*zr + zi*zi <= 4.0f && iter < MAX_ITER) {
                float temp = zr*zr - zi*zi + cr;
                zi = 2.0f * zr * zi + ci;
                zr = temp;
                iter++;
            }

            // 発散速度(収束度)に応じて文字を変える(アスキーアートの階調表現)
            if (iter == MAX_ITER) {
                printf("#"); // 集合の内部(収束)
            } else if (iter > 20) {
                printf("@");
            } else if (iter > 10) {
                printf("%c", ".-:+=*"[iter % 6]);
            } else if (iter > 5) {
                printf(".");
            } else {
                printf(" "); // すぐに発散した場所
            }
        }
        printf("\r\n"); // 行の終わり(UARTなので\r\nが必要)
    }
    printf("\r\nDone!\r\n");
}

回転砲塔型スペーススクロールシューティング2  弾も回転版 【MachiKania Type PU】


・弾も回転とスクロールに対応シてみました。
 ------------以下、操作方法は前回と同じ---------
・操作は下記の通り
 左右ボタンで方向変更
 Fireボタンで弾を発射
 ※キーボード使用時は、MACHIKAP.INIファイルのEMULATEBUTTONの#を除いて、キーボードでボタンが使えるようにしてください。
  FireボタンはキーボードではFキーになります。
 SCROLL_SPEED#が縦スクロールの速さになります。その他、パラメーターをいろいろ変えて楽しんでみてください。

USEGRAPHIC
USEVAR SW,SH,SHIPX#,SHIPY#,SHIPR,MAXBULLETS,MAXASTEROIDS,BULLETSPEED#,SCORE,LIVES,GAMEOVER,ASTEROIDMINSIZE,ASTEROIDMAXSIZE,ASTEROIDMINSPEED#,ASTEROIDMAXSPEED#,DX#,DY#,DIST#,COLORWHITE,COLORBLACK,BULLETSX#,BULLETSY#,BULLETVX#,BULLETVY#,BULLETACTIVE,ASTEROIDSX#,ASTEROIDSY#,ASTEROIDVX#,ASTEROIDVY#,ASTEROIDSIZE#,ASTEROIDACTIVE,SIDE,ROTATE_DEG#,COSROT#,SINROT#,NEWX#,NEWY#,STARSX#,STARSY#,NUMSTARS,SCROLL_SPEED#
DIM BULLETSX#(20)
DIM BULLETSY#(20)
DIM BULLETVX#(20)
DIM BULLETVY#(20)
DIM BULLETACTIVE(20)
DIM ASTEROIDSX#(8)
DIM ASTEROIDSY#(8)
DIM ASTEROIDVX#(8)
DIM ASTEROIDVY#(8)
DIM ASTEROIDSIZE#(8)
DIM ASTEROIDACTIVE(8)
DIM STARSX#(100)
DIM STARSY#(100)
REM Initialize constants
SW=336
SH=216
SHIPX#=FLOAT#(SW)/2
SHIPY#=FLOAT#(SH)/5*4
SHIPR=8
MAXBULLETS=20
MAXASTEROIDS=8
BULLETSPEED#=3
ASTEROIDMINSIZE=6
ASTEROIDMAXSIZE=16
ASTEROIDMINSPEED#=1.5
ASTEROIDMAXSPEED#=2.0
ROTATE_DEG#=1
NUMSTARS=100
SCROLL_SPEED#=0.8
COLORWHITE=255
COLORBLACK=0
REM Initialize game variables
SCORE=0
LIVES=3
GAMEOVER=0
REM Clear arrays
FOR I=0 TO MAXBULLETS-1
BULLETACTIVE(I)=0
NEXT
FOR I=0 TO MAXASTEROIDS-1
ASTEROIDACTIVE(I)=0
NEXT
REM Initialize stars (random positions)
FOR I=0 TO NUMSTARS-1
  STARSX#(I)=FLOAT#(RND() % SW)
  STARSY#(I)=FLOAT#(RND() % SH)
NEXT
REM Generate initial asteroids
FOR I=0 TO 3
GOSUB GENERATEASTEROID
NEXT
REM Main game loop
WHILE GAMEOVER=0
GCLS
REM Handle rotation input (隕石、星、弾を回転)
IF KEYS(4) THEN
  COSROT#=COS#(ROTATE_DEG#*PI#/180)
  SINROT#=SIN#(ROTATE_DEG#*PI#/180)
  REM Rotate asteroids
  FOR I=0 TO MAXASTEROIDS-1
    IF ASTEROIDACTIVE(I)=1 THEN
      NEWX#=ASTEROIDSX#(I)-SHIPX#
      NEWY#=ASTEROIDSY#(I)-SHIPY#
      ASTEROIDSX#(I)=NEWX#*COSROT#-NEWY#*SINROT#+SHIPX#
      ASTEROIDSY#(I)=NEWX#*SINROT#+NEWY#*COSROT#+SHIPY#
    ENDIF
  NEXT
  REM Rotate stars
  FOR I=0 TO NUMSTARS-1
    NEWX#=STARSX#(I)-SHIPX#
    NEWY#=STARSY#(I)-SHIPY#
    STARSX#(I)=NEWX#*COSROT#-NEWY#*SINROT#+SHIPX#
    STARSY#(I)=NEWX#*SINROT#+NEWY#*COSROT#+SHIPY#
  NEXT
  REM Rotate bullets
  FOR I=0 TO MAXBULLETS-1
    IF BULLETACTIVE(I)=1 THEN
      NEWX#=BULLETSX#(I)-SHIPX#
      NEWY#=BULLETSY#(I)-SHIPY#
      BULLETSX#(I)=NEWX#*COSROT#-NEWY#*SINROT#+SHIPX#
      BULLETSY#(I)=NEWX#*SINROT#+NEWY#*COSROT#+SHIPY#
    ENDIF
  NEXT
ENDIF
IF KEYS(8) THEN
  COSROT#=COS#(-ROTATE_DEG#*PI#/180)
  SINROT#=SIN#(-ROTATE_DEG#*PI#/180)
  REM Rotate asteroids
  FOR I=0 TO MAXASTEROIDS-1
    IF ASTEROIDACTIVE(I)=1 THEN
      NEWX#=ASTEROIDSX#(I)-SHIPX#
      NEWY#=ASTEROIDSY#(I)-SHIPY#
      ASTEROIDSX#(I)=NEWX#*COSROT#-NEWY#*SINROT#+SHIPX#
      ASTEROIDSY#(I)=NEWX#*SINROT#+NEWY#*COSROT#+SHIPY#
    ENDIF
  NEXT
  REM Rotate stars
  FOR I=0 TO NUMSTARS-1
    NEWX#=STARSX#(I)-SHIPX#
    NEWY#=STARSY#(I)-SHIPY#
    STARSX#(I)=NEWX#*COSROT#-NEWY#*SINROT#+SHIPX#
    STARSY#(I)=NEWX#*SINROT#+NEWY#*COSROT#+SHIPY#
  NEXT
  REM Rotate bullets
  FOR I=0 TO MAXBULLETS-1
    IF BULLETACTIVE(I)=1 THEN
      NEWX#=BULLETSX#(I)-SHIPX#
      NEWY#=BULLETSY#(I)-SHIPY#
      BULLETSX#(I)=NEWX#*COSROT#-NEWY#*SINROT#+SHIPX#
      BULLETSY#(I)=NEWX#*SINROT#+NEWY#*COSROT#+SHIPY#
    ENDIF
  NEXT
ENDIF
REM Scroll asteroids, stars, and bullets downward
FOR I=0 TO MAXASTEROIDS-1
  IF ASTEROIDACTIVE(I)=1 THEN
    ASTEROIDSY#(I)=ASTEROIDSY#(I)+SCROLL_SPEED#
  ENDIF
NEXT
FOR I=0 TO NUMSTARS-1
  STARSY#(I)=STARSY#(I)+SCROLL_SPEED#
  IF STARSY#(I)>FLOAT#(SH) THEN STARSY#(I)=STARSY#(I)-FLOAT#(SH)
NEXT
FOR I=0 TO MAXBULLETS-1
  IF BULLETACTIVE(I)=1 THEN
    BULLETSY#(I)=BULLETSY#(I)+SCROLL_SPEED#
  ENDIF
NEXT
REM Draw stars (background)
GCOLOR COLORWHITE
FOR I=0 TO NUMSTARS-1
  PSET INT(STARSX#(I)),INT(STARSY#(I)),COLORWHITE
NEXT
REM Draw cannon (fixed, always pointing up)
GCOLOR COLORWHITE
CIRCLE INT(SHIPX#),INT(SHIPY#),SHIPR,COLORWHITE
LINE INT(SHIPX#),INT(SHIPY#),INT(SHIPX#),INT(SHIPY#-20),COLORWHITE
REM Handle fire
IF KEYS(32) THEN GOSUB FIREBULLET
REM Update bullets (upward movement relative to scroll)
FOR I=0 TO MAXBULLETS-1
IF BULLETACTIVE(I)=1 THEN
  BULLETSY#(I)=BULLETSY#(I)-BULLETSPEED#
  IF BULLETSY#(I)<0 THEN BULLETACTIVE(I)=0
ENDIF
NEXT
REM Update asteroids (additional movement beyond scroll)
FOR I=0 TO MAXASTEROIDS-1
IF ASTEROIDACTIVE(I)=1 THEN
  ASTEROIDSX#(I)=ASTEROIDSX#(I)+ASTEROIDVX#(I)
  ASTEROIDSY#(I)=ASTEROIDSY#(I)+ASTEROIDVY#(I)
  IF ASTEROIDSY#(I)>FLOAT#(SH)+20 OR ASTEROIDSX#(I)<FLOAT#(-20) OR ASTEROIDSX#(I)>FLOAT#(SW)+20 THEN
    ASTEROIDACTIVE(I)=0
    GOSUB GENERATEASTEROID
  ENDIF
ENDIF
NEXT
REM Check bullet-asteroid collision
FOR I=0 TO MAXBULLETS-1
IF BULLETACTIVE(I)=1 THEN
  FOR J=0 TO MAXASTEROIDS-1
    IF ASTEROIDACTIVE(J)=1 THEN
      DX#=BULLETSX#(I)-ASTEROIDSX#(J)
      DY#=BULLETSY#(I)-ASTEROIDSY#(J)
      DIST#=SQRT#(DX#*DX#+DY#*DY#)
      IF DIST#<ASTEROIDSIZE#(J)+FLOAT#(3) THEN
        BULLETACTIVE(I)=0
        ASTEROIDACTIVE(J)=0
        SCORE=SCORE+10
        GOSUB GENERATEASTEROID
      ENDIF
    ENDIF
  NEXT
ENDIF
NEXT
REM Check ship-asteroid collision
FOR I=0 TO MAXASTEROIDS-1
IF ASTEROIDACTIVE(I)=1 THEN
  DX#=SHIPX#-ASTEROIDSX#(I)
  DY#=SHIPY#-ASTEROIDSY#(I)
  DIST#=SQRT#(DX#*DX#+DY#*DY#)
  IF DIST#<FLOAT#(SHIPR)+ASTEROIDSIZE#(I) THEN
    LIVES=LIVES-1
    ASTEROIDACTIVE(I)=0
    GOSUB GENERATEASTEROID
    IF LIVES<=0 THEN GAMEOVER=1
  ENDIF
ENDIF
NEXT
REM Draw bullets
FOR I=0 TO MAXBULLETS-1
IF BULLETACTIVE(I)=1 THEN
  CIRCLEFILL INT(BULLETSX#(I)),INT(BULLETSY#(I)),3,COLORWHITE
ENDIF
NEXT
REM Draw asteroids
FOR I=0 TO MAXASTEROIDS-1
IF ASTEROIDACTIVE(I)=1 THEN
  CIRCLE INT(ASTEROIDSX#(I)),INT(ASTEROIDSY#(I)),INT(ASTEROIDSIZE#(I)),COLORWHITE
ENDIF
NEXT
REM Draw HUD
GPRINT 0,0,COLORWHITE,COLORBLACK,"SCORE:"+DEC$(SCORE)
GPRINT 0,10,COLORWHITE,COLORBLACK,"LIVES:"+DEC$(LIVES)
WAIT 1
WEND
GCLS
GPRINT SW/2-50,SH/2,COLORWHITE,COLORBLACK,"GAME OVER"
GPRINT SW/2-50,SH/2+10,COLORWHITE,COLORBLACK,"SCORE:"+DEC$(SCORE)
WAIT 300
END
LABEL FIREBULLET
FOR I=0 TO MAXBULLETS-1
IF BULLETACTIVE(I)=0 THEN
  BULLETACTIVE(I)=1
  BULLETSX#(I)=SHIPX#
  BULLETSY#(I)=SHIPY#-20
  BULLETVX#(I)=0
  BULLETVY#(I)=-BULLETSPEED#
  RETURN
ENDIF
NEXT
RETURN
LABEL GENERATEASTEROID
FOR I=0 TO MAXASTEROIDS-1
IF ASTEROIDACTIVE(I)=0 THEN
  ASTEROIDACTIVE(I)=1
  SIDE=RND() % 3
  IF SIDE=0 THEN
    ASTEROIDSX#(I)=FLOAT#(-20)
    ASTEROIDSY#(I)=FLOAT#(RND() % SH)
    ASTEROIDVX#(I)=ASTEROIDMINSPEED#+(FLOAT#(RND())/32768)*(ASTEROIDMAXSPEED#-ASTEROIDMINSPEED#)
    ASTEROIDVY#(I)=(FLOAT#(RND())/32768)*2-1
  ELSEIF SIDE=1 THEN
    ASTEROIDSX#(I)=FLOAT#(SW)+20
    ASTEROIDSY#(I)=FLOAT#(RND() % SH)
    ASTEROIDVX#(I)=-(ASTEROIDMINSPEED#+(FLOAT#(RND())/32768)*(ASTEROIDMAXSPEED#-ASTEROIDMINSPEED#))
    ASTEROIDVY#(I)=(FLOAT#(RND())/32768)*2-1
  ELSE
    ASTEROIDSX#(I)=FLOAT#(RND() % SW)
    ASTEROIDSY#(I)=FLOAT#(-20)
    ASTEROIDVX#(I)=(FLOAT#(RND())/32768)*4-2
    ASTEROIDVY#(I)=ASTEROIDMINSPEED#+(FLOAT#(RND())/32768)*(ASTEROIDMAXSPEED#-ASTEROIDMINSPEED#)
  ENDIF
  ASTEROIDSIZE#(I)=FLOAT#(ASTEROIDMINSIZE)+FLOAT#(INT((FLOAT#(RND())/32768)*(FLOAT#(ASTEROIDMAXSIZE)-FLOAT#(ASTEROIDMINSIZE))))
  RETURN
ENDIF
NEXT
RETURN

回転砲塔型スペーススクロールシューティング!!  【MachiKania Type PU】


・今度は砲台を回転させると宇宙空間が回転するスクロールゲームです。
  上の画像で見ると前回と同じようですが、感じは全然違います。
・操作は下記の通り
 左右ボタンで方向変更
 Fireボタンで弾を発射
 ※キーボード使用時は、MACHIKAP.INIファイルのEMULATEBUTTONの#を除いて、キーボードでボタンが使えるようにしてください。
  FireボタンはキーボードではFキーになります。
 SCROLL_SPEED#が縦スクロールの速さになります。その他、パラメーターをいろいろ変えて楽しんでみてください。

USEGRAPHIC
USEVAR SW,SH,SHIPX#,SHIPY#,SHIPR,MAXBULLETS,MAXASTEROIDS,BULLETSPEED#,SCORE,LIVES,GAMEOVER,ASTEROIDMINSIZE,ASTEROIDMAXSIZE,ASTEROIDMINSPEED#,ASTEROIDMAXSPEED#,DX#,DY#,DIST#,COLORWHITE,COLORBLACK,BULLETSX#,BULLETSY#,BULLETVX#,BULLETVY#,BULLETACTIVE,ASTEROIDSX#,ASTEROIDSY#,ASTEROIDVX#,ASTEROIDVY#,ASTEROIDSIZE#,ASTEROIDACTIVE,SIDE,ROTATE_DEG#,COSROT#,SINROT#,NEWX#,NEWY#,STARSX#,STARSY#,NUMSTARS,SCROLL_SPEED#
DIM BULLETSX#(20)
DIM BULLETSY#(20)
DIM BULLETVX#(20)
DIM BULLETVY#(20)
DIM BULLETACTIVE(20)
DIM ASTEROIDSX#(8)
DIM ASTEROIDSY#(8)
DIM ASTEROIDVX#(8)
DIM ASTEROIDVY#(8)
DIM ASTEROIDSIZE#(8)
DIM ASTEROIDACTIVE(8)
DIM STARSX#(100)
DIM STARSY#(100)
REM Initialize constants
SW=336
SH=216
SHIPX#=FLOAT#(SW)/2
SHIPY#=FLOAT#(SH)/5*4
SHIPR=8
MAXBULLETS=20
MAXASTEROIDS=8
BULLETSPEED#=3
ASTEROIDMINSIZE=6
ASTEROIDMAXSIZE=16
ASTEROIDMINSPEED#=1.5
ASTEROIDMAXSPEED#=2.0
ROTATE_DEG#=1
NUMSTARS=100
SCROLL_SPEED#=0.8
COLORWHITE=255
COLORBLACK=0
REM Initialize game variables
SCORE=0
LIVES=3
GAMEOVER=0
REM Clear arrays
FOR I=0 TO MAXBULLETS-1
BULLETACTIVE(I)=0
NEXT
FOR I=0 TO MAXASTEROIDS-1
ASTEROIDACTIVE(I)=0
NEXT
REM Initialize stars (random positions)
FOR I=0 TO NUMSTARS-1
  STARSX#(I)=FLOAT#(RND() % SW)
  STARSY#(I)=FLOAT#(RND() % SH)
NEXT
REM Generate initial asteroids
FOR I=0 TO 3
GOSUB GENERATEASTEROID
NEXT
REM Main game loop
WHILE GAMEOVER=0
GCLS
REM Handle rotation input (隕石と星を回転)
IF KEYS(4) THEN
  COSROT#=COS#(ROTATE_DEG#*PI#/180)
  SINROT#=SIN#(ROTATE_DEG#*PI#/180)
  REM Rotate asteroids
  FOR I=0 TO MAXASTEROIDS-1
    IF ASTEROIDACTIVE(I)=1 THEN
      NEWX#=ASTEROIDSX#(I)-SHIPX#
      NEWY#=ASTEROIDSY#(I)-SHIPY#
      ASTEROIDSX#(I)=NEWX#*COSROT#-NEWY#*SINROT#+SHIPX#
      ASTEROIDSY#(I)=NEWX#*SINROT#+NEWY#*COSROT#+SHIPY#
    ENDIF
  NEXT
  REM Rotate stars
  FOR I=0 TO NUMSTARS-1
    NEWX#=STARSX#(I)-SHIPX#
    NEWY#=STARSY#(I)-SHIPY#
    STARSX#(I)=NEWX#*COSROT#-NEWY#*SINROT#+SHIPX#
    STARSY#(I)=NEWX#*SINROT#+NEWY#*COSROT#+SHIPY#
  NEXT
ENDIF
IF KEYS(8) THEN
  COSROT#=COS#(-ROTATE_DEG#*PI#/180)
  SINROT#=SIN#(-ROTATE_DEG#*PI#/180)
  REM Rotate asteroids
  FOR I=0 TO MAXASTEROIDS-1
    IF ASTEROIDACTIVE(I)=1 THEN
      NEWX#=ASTEROIDSX#(I)-SHIPX#
      NEWY#=ASTEROIDSY#(I)-SHIPY#
      ASTEROIDSX#(I)=NEWX#*COSROT#-NEWY#*SINROT#+SHIPX#
      ASTEROIDSY#(I)=NEWX#*SINROT#+NEWY#*COSROT#+SHIPY#
    ENDIF
  NEXT
  REM Rotate stars
  FOR I=0 TO NUMSTARS-1
    NEWX#=STARSX#(I)-SHIPX#
    NEWY#=STARSY#(I)-SHIPY#
    STARSX#(I)=NEWX#*COSROT#-NEWY#*SINROT#+SHIPX#
    STARSY#(I)=NEWX#*SINROT#+NEWY#*COSROT#+SHIPY#
  NEXT
ENDIF
REM Scroll asteroids and stars downward
FOR I=0 TO MAXASTEROIDS-1
  IF ASTEROIDACTIVE(I)=1 THEN
    ASTEROIDSY#(I)=ASTEROIDSY#(I)+SCROLL_SPEED#
  ENDIF
NEXT
FOR I=0 TO NUMSTARS-1
  STARSY#(I)=STARSY#(I)+SCROLL_SPEED#
  IF STARSY#(I)>FLOAT#(SH) THEN STARSY#(I)=STARSY#(I)-FLOAT#(SH)
NEXT
REM Draw stars (background)
GCOLOR COLORWHITE
FOR I=0 TO NUMSTARS-1
  PSET INT(STARSX#(I)),INT(STARSY#(I)),COLORWHITE
NEXT
REM Draw cannon (fixed, always pointing up)
GCOLOR COLORWHITE
CIRCLE INT(SHIPX#),INT(SHIPY#),SHIPR,COLORWHITE
LINE INT(SHIPX#),INT(SHIPY#),INT(SHIPX#),INT(SHIPY#-20),COLORWHITE
REM Handle fire
IF KEYS(32) THEN GOSUB FIREBULLET
REM Update bullets
FOR I=0 TO MAXBULLETS-1
IF BULLETACTIVE(I)=1 THEN
  BULLETSY#(I)=BULLETSY#(I)-BULLETSPEED#
  IF BULLETSY#(I)<0 THEN BULLETACTIVE(I)=0
ENDIF
NEXT
REM Update asteroids
FOR I=0 TO MAXASTEROIDS-1
IF ASTEROIDACTIVE(I)=1 THEN
  ASTEROIDSX#(I)=ASTEROIDSX#(I)+ASTEROIDVX#(I)
  ASTEROIDSY#(I)=ASTEROIDSY#(I)+ASTEROIDVY#(I)
  IF ASTEROIDSY#(I)>FLOAT#(SH)+20 OR ASTEROIDSX#(I)<FLOAT#(-20) OR ASTEROIDSX#(I)>FLOAT#(SW)+20 THEN
    ASTEROIDACTIVE(I)=0
    GOSUB GENERATEASTEROID
  ENDIF
ENDIF
NEXT
REM Check bullet-asteroid collision
FOR I=0 TO MAXBULLETS-1
IF BULLETACTIVE(I)=1 THEN
  FOR J=0 TO MAXASTEROIDS-1
    IF ASTEROIDACTIVE(J)=1 THEN
      DX#=BULLETSX#(I)-ASTEROIDSX#(J)
      DY#=BULLETSY#(I)-ASTEROIDSY#(J)
      DIST#=SQRT#(DX#*DX#+DY#*DY#)
      IF DIST#<ASTEROIDSIZE#(J)+FLOAT#(3) THEN
        BULLETACTIVE(I)=0
        ASTEROIDACTIVE(J)=0
        SCORE=SCORE+10
        GOSUB GENERATEASTEROID
      ENDIF
    ENDIF
  NEXT
ENDIF
NEXT
REM Check ship-asteroid collision
FOR I=0 TO MAXASTEROIDS-1
IF ASTEROIDACTIVE(I)=1 THEN
  DX#=SHIPX#-ASTEROIDSX#(I)
  DY#=SHIPY#-ASTEROIDSY#(I)
  DIST#=SQRT#(DX#*DX#+DY#*DY#)
  IF DIST#<FLOAT#(SHIPR)+ASTEROIDSIZE#(I) THEN
    LIVES=LIVES-1
    ASTEROIDACTIVE(I)=0
    GOSUB GENERATEASTEROID
    IF LIVES<=0 THEN GAMEOVER=1
  ENDIF
ENDIF
NEXT
REM Draw bullets
FOR I=0 TO MAXBULLETS-1
IF BULLETACTIVE(I)=1 THEN
  CIRCLEFILL INT(BULLETSX#(I)),INT(BULLETSY#(I)),3,COLORWHITE
ENDIF
NEXT
REM Draw asteroids
FOR I=0 TO MAXASTEROIDS-1
IF ASTEROIDACTIVE(I)=1 THEN
  CIRCLE INT(ASTEROIDSX#(I)),INT(ASTEROIDSY#(I)),INT(ASTEROIDSIZE#(I)),COLORWHITE
ENDIF
NEXT
REM Draw HUD
GPRINT 0,0,COLORWHITE,COLORBLACK,"SCORE:"+DEC$(SCORE)
GPRINT 0,10,COLORWHITE,COLORBLACK,"LIVES:"+DEC$(LIVES)
WAIT 1
WEND
GCLS
GPRINT SW/2-50,SH/2,COLORWHITE,COLORBLACK,"GAME OVER"
GPRINT SW/2-50,SH/2+10,COLORWHITE,COLORBLACK,"SCORE:"+DEC$(SCORE)
WAIT 300
END
LABEL FIREBULLET
FOR I=0 TO MAXBULLETS-1
IF BULLETACTIVE(I)=0 THEN
  BULLETACTIVE(I)=1
  BULLETSX#(I)=SHIPX#
  BULLETSY#(I)=SHIPY#-20
  BULLETVX#(I)=0
  BULLETVY#(I)=-BULLETSPEED#
  RETURN
ENDIF
NEXT
RETURN
LABEL GENERATEASTEROID
FOR I=0 TO MAXASTEROIDS-1
IF ASTEROIDACTIVE(I)=0 THEN
  ASTEROIDACTIVE(I)=1
  SIDE=RND() % 3
  IF SIDE=0 THEN
    ASTEROIDSX#(I)=FLOAT#(-20)
    ASTEROIDSY#(I)=FLOAT#(RND() % SH)
    ASTEROIDVX#(I)=ASTEROIDMINSPEED#+(FLOAT#(RND())/32768)*(ASTEROIDMAXSPEED#-ASTEROIDMINSPEED#)
    ASTEROIDVY#(I)=(FLOAT#(RND())/32768)*2-1
  ELSEIF SIDE=1 THEN
    ASTEROIDSX#(I)=FLOAT#(SW)+20
    ASTEROIDSY#(I)=FLOAT#(RND() % SH)
    ASTEROIDVX#(I)=-(ASTEROIDMINSPEED#+(FLOAT#(RND())/32768)*(ASTEROIDMAXSPEED#-ASTEROIDMINSPEED#))
    ASTEROIDVY#(I)=(FLOAT#(RND())/32768)*2-1
  ELSE
    ASTEROIDSX#(I)=FLOAT#(RND() % SW)
    ASTEROIDSY#(I)=FLOAT#(-20)
    ASTEROIDVX#(I)=(FLOAT#(RND())/32768)*4-2
    ASTEROIDVY#(I)=ASTEROIDMINSPEED#+(FLOAT#(RND())/32768)*(ASTEROIDMAXSPEED#-ASTEROIDMINSPEED#)
  ENDIF
  ASTEROIDSIZE#(I)=FLOAT#(ASTEROIDMINSIZE)+FLOAT#(INT((FLOAT#(RND())/32768)*(FLOAT#(ASTEROIDMAXSIZE)-FLOAT#(ASTEROIDMINSIZE))))
  RETURN
ENDIF
NEXT
RETURN

シンプルアステロイド【Machikania Type PU】

・砲台を回転させて弾を打って小惑星を破壊するレトロゲームです。
・本物のような円盤は飛んできません(*^^*);
・操作は下記の通り
 左右ボタンで方向変更
 Fireボタンで迎撃
 上ボタンで加速
 減速キーは… ない…😱😱
 逆方向に方向転換してから再加速して減速…
 ※キーボード使用時は、MACHIKAP.INIファイルのEMULATEBUTTONの#を除いて、キーボードでボタンが使えるようにしてください。
  FireボタンはキーボードFキーになります。

USEGRAPHIC
USEVAR SW,SH,SHIPX#,SHIPY#,SHIPVX#,SHIPVY#,SHIPANGLE#,SHIPR#,MAXBULLETS,MAXASTEROIDS,BULLETSPEED#,SHIPSPEED#,ROTSPEED#,SCORE,LIVES,GAMEOVER,ASTEROIDMINSIZE#,ASTEROIDMAXSIZE#,ASTEROIDMINSPEED#,ASTEROIDMAXSPEED#,DX#,DY#,DIST#,RAD#,COLORWHITE,COLORBLACK,BULLETSX#,BULLETSY#,BULLETVX#,BULLETVY#,BULLETACTIVE,ASTEROIDSX#,ASTEROIDSY#,ASTEROIDVX#,ASTEROIDVY#,ASTEROIDSIZE#,ASTEROIDACTIVE

DIM BULLETSX#(10)
DIM BULLETSY#(10)
DIM BULLETVX#(10)
DIM BULLETVY#(10)
DIM BULLETACTIVE(10)
DIM ASTEROIDSX#(5)
DIM ASTEROIDSY#(5)
DIM ASTEROIDVX#(5)
DIM ASTEROIDVY#(5)
DIM ASTEROIDSIZE#(5)
DIM ASTEROIDACTIVE(5)

REM Initialize constants
SW=336
SH=216
SHIPR#=5
MAXBULLETS=10
MAXASTEROIDS=5
BULLETSPEED#=5
SHIPSPEED#=0.2
ROTSPEED#=5
ASTEROIDMINSIZE#=5
ASTEROIDMAXSIZE#=15
ASTEROIDMINSPEED#=0.5
ASTEROIDMAXSPEED#=1.5
COLORWHITE=255
COLORBLACK=0

REM Initialize game variables
SHIPX#=FLOAT#(SW)/2
SHIPY#=FLOAT#(SH)/2
SHIPVX#=0
SHIPVY#=0
SHIPANGLE#=0
SCORE=0
LIVES=3
GAMEOVER=0
REM Clear arrays
FOR I=0 TO MAXBULLETS-1
BULLETACTIVE(I)=0
NEXT
FOR I=0 TO MAXASTEROIDS-1
ASTEROIDACTIVE(I)=0
NEXT

REM Generate initial asteroids
FOR I=0 TO 3
GOSUB GENERATEASTEROID
NEXT

REM Main game loop
WHILE GAMEOVER=0
GCLS

REM Handle input
IF KEYS(4) THEN SHIPANGLE#=SHIPANGLE#-ROTSPEED#
IF KEYS(8) THEN SHIPANGLE#=SHIPANGLE#+ROTSPEED#
IF SHIPANGLE#<0 THEN SHIPANGLE#=FMOD#(SHIPANGLE#+360,360)
IF SHIPANGLE#>=360 THEN SHIPANGLE#=FMOD#(SHIPANGLE#,360)
IF KEYS(1) THEN
RAD#=SHIPANGLE#*PI#/180
SHIPVX#=SHIPVX#+COS#(RAD#)*SHIPSPEED#
SHIPVY#=SHIPVY#+SIN#(RAD#)*SHIPSPEED#
ENDIF
IF KEYS(32) THEN GOSUB FIREBULLET

REM Update ship
SHIPX#=SHIPX#+SHIPVX#
SHIPY#=SHIPY#+SHIPVY#
IF SHIPX#<0 THEN SHIPX#=SHIPX#+FLOAT#(SW)
IF SHIPX#>=FLOAT#(SW) THEN SHIPX#=FMOD#(SHIPX#,FLOAT#(SW))
IF SHIPY#<0 THEN SHIPY#=SHIPY#+FLOAT#(SH)
IF SHIPY#>=FLOAT#(SH) THEN SHIPY#=FMOD#(SHIPY#,FLOAT#(SH))

REM Update bullets
FOR I=0 TO MAXBULLETS-1
IF BULLETACTIVE(I)=1 THEN
BULLETSX#(I)=BULLETSX#(I)+BULLETVX#(I)
BULLETSY#(I)=BULLETSY#(I)+BULLETVY#(I)
IF BULLETSX#(I)<0 OR BULLETSX#(I)>=FLOAT#(SW) OR BULLETSY#(I)<0 OR BULLETSY#(I)>=FLOAT#(SH) THEN BULLETACTIVE(I)=0
ENDIF
NEXT

REM Update asteroids
FOR I=0 TO MAXASTEROIDS-1
IF ASTEROIDACTIVE(I)=1 THEN
ASTEROIDSX#(I)=ASTEROIDSX#(I)+ASTEROIDVX#(I)
ASTEROIDSY#(I)=ASTEROIDSY#(I)+ASTEROIDVY#(I)
IF ASTEROIDSX#(I)<0 THEN ASTEROIDSX#(I)=ASTEROIDSX#(I)+FLOAT#(SW)
IF ASTEROIDSX#(I)>=FLOAT#(SW) THEN ASTEROIDSX#(I)=FMOD#(ASTEROIDSX#(I),FLOAT#(SW))
IF ASTEROIDSY#(I)<0 THEN ASTEROIDSY#(I)=ASTEROIDSY#(I)+FLOAT#(SH)
IF ASTEROIDSY#(I)>=FLOAT#(SH) THEN ASTEROIDSY#(I)=FMOD#(ASTEROIDSY#(I),FLOAT#(SH))
ENDIF
NEXT

REM Bullet-Asteroid collision
FOR I=0 TO MAXBULLETS-1
IF BULLETACTIVE(I)=1 THEN
FOR J=0 TO MAXASTEROIDS-1
IF ASTEROIDACTIVE(J)=1 THEN
DX#=BULLETSX#(I)-ASTEROIDSX#(J)
DY#=BULLETSY#(I)-ASTEROIDSY#(J)
DIST#=SQRT#(DX#*DX#+DY#*DY#)
IF DIST#<ASTEROIDSIZE#(J) THEN
BULLETACTIVE(I)=0
ASTEROIDACTIVE(J)=0
SCORE=SCORE+10
GOSUB GENERATEASTEROID
ENDIF
ENDIF
NEXT
ENDIF
NEXT

REM Ship-Asteroid collision
FOR I=0 TO MAXASTEROIDS-1
IF ASTEROIDACTIVE(I)=1 THEN
DX#=SHIPX#-ASTEROIDSX#(I)
DY#=SHIPY#-ASTEROIDSY#(I)
DIST#=SQRT#(DX#*DX#+DY#*DY#)
IF DIST#<SHIPR#+ASTEROIDSIZE#(I) THEN
LIVES=LIVES-1
ASTEROIDACTIVE(I)=0
GOSUB GENERATEASTEROID
IF LIVES<=0 THEN GAMEOVER=1
ENDIF
ENDIF
NEXT

REM Draw everything
GCOLOR COLORWHITE
RAD#=SHIPANGLE#*PI#/180
CIRCLE INT(SHIPX#),INT(SHIPY#),INT(SHIPR#),COLORWHITE
LINE INT(SHIPX#),INT(SHIPY#),INT(SHIPX#+COS#(RAD#)*10),INT(SHIPY#+SIN#(RAD#)*10),COLORWHITE
FOR I=0 TO MAXBULLETS-1
IF BULLETACTIVE(I)=1 THEN PSET INT(BULLETSX#(I)),INT(BULLETSY#(I)),COLORWHITE
NEXT
FOR I=0 TO MAXASTEROIDS-1
IF ASTEROIDACTIVE(I)=1 THEN CIRCLE INT(ASTEROIDSX#(I)),INT(ASTEROIDSY#(I)),INT(ASTEROIDSIZE#(I)),COLORWHITE
NEXT
GPRINT 0,0,COLORWHITE,COLORBLACK,"SCORE:"+DEC$(SCORE)
GPRINT 0,8,COLORWHITE,COLORBLACK,"LIVES:"+DEC$(LIVES)
WAIT 1
WEND
GCLS
GPRINT SW/2-40,SH/2,COLORWHITE,COLORBLACK,"GAME OVER"
GPRINT SW/2-40,SH/2+8,COLORWHITE,COLORBLACK,"SCORE:"+DEC$(SCORE)
WAIT 300
END

LABEL FIREBULLET
FOR I=0 TO MAXBULLETS-1
IF BULLETACTIVE(I)=0 THEN
BULLETACTIVE(I)=1
BULLETSX#(I)=SHIPX#
BULLETSY#(I)=SHIPY#
RAD#=SHIPANGLE#*PI#/180
BULLETVX#(I)=COS#(RAD#)*BULLETSPEED#
BULLETVY#(I)=SIN#(RAD#)*BULLETSPEED#
RETURN
ENDIF
NEXT
RETURN

LABEL GENERATEASTEROID
FOR I=0 TO MAXASTEROIDS-1
IF ASTEROIDACTIVE(I)=0 THEN
ASTEROIDACTIVE(I)=1
IF RND() % 2 = 0 THEN
  ASTEROIDSX#(I) = FLOAT#(RND() % SW)
  IF RND() % 2 = 0 THEN
    ASTEROIDSY#(I) = 0
  ELSE
    ASTEROIDSY#(I) = FLOAT#(SH-1)
  ENDIF
ELSE
  ASTEROIDSY#(I) = FLOAT#(RND() % SH)
  IF RND() % 2 = 0 THEN
    ASTEROIDSX#(I) = 0
  ELSE
    ASTEROIDSX#(I) = FLOAT#(SW-1)
  ENDIF
ENDIF
ASTEROIDVX#(I)=(FLOAT#(RND())/32768)*(ASTEROIDMAXSPEED#-ASTEROIDMINSPEED#)+ASTEROIDMINSPEED#
IF RND() % 2=0 THEN ASTEROIDVX#(I)=-ASTEROIDVX#(I)
ASTEROIDVY#(I)=(FLOAT#(RND())/32768)*(ASTEROIDMAXSPEED#-ASTEROIDMINSPEED#)+ASTEROIDMINSPEED#
IF RND() % 2=0 THEN ASTEROIDVY#(I)=-ASTEROIDVY#(I)
ASTEROIDSIZE#(I)=FLOAT#(INT((FLOAT#(RND())/32768)*(ASTEROIDMAXSIZE#-ASTEROIDMINSIZE#)))+ASTEROIDMINSIZE#
RETURN
ENDIF
NEXT
RETURN