#ワード文書1でシューティングゲームは作成可能か#

VBAアクションゲーム?
 
Wordで動かそう!

ここは新世紀VBAの終着港Wordで動かそう!へようこそ!ワードはアクションゲーム作成に不向き?・・・おそらく。
カーソル動かしテキスト表現、挿入削除で書き替える。開発環境VBAがもれなくついてるワードだけど、
Word上で動かすことに意義があるとは言い切れない。

元祖VBAアクションゲーム?Excelで動かそう!はこちら

since 2004/6/12 リンクフリー

合計 本日 昨日 

マイクロソフト社のワープロソフト「Word」の文書上でVBAマクロを使用しアクションゲームを作ってみます。題して
 

ワード文書1でシューティングゲームは作成可能か


文書1シューティングVer1.00のダウンロード
※「Z」キーで強制終了を追加

1 さて、どこから手を着けようか

 Wordマクロなんぞ使ったことない私、でも大丈夫。マクロ記録があるからねえ。このへんがVBAの素晴らしいところであって、とにかく操作を記録できさえすれば勝利という。多分マニュアル読む必要もないぞよ。そこで決めた。教科書はマクロ記録のみ。VBAの素晴らしさを体感するためにもヘルプを一切見ずWordでアクションゲームを作る、ことにします。

2 どんなゲームにする?

最初から激しいのは無理でしょうねえ。よってWordらしくテキストベースの簡単なシューティングゲームを作ろう。自機も敵も弾もテキストで。あのExcelテキストゲーム名作「只今インベーダー」の簡易版のイメージ。無料タイピングゲーム配布で有名な猫エク谷さんのオリジナルExcel版はこちら

    只

    *

      凸

3 さあ、作り始めよう

兎にも角にもマクロ記録だ。どうやったらテキストを表示させることができるのか想像もつかん。まずはマクロ記録の開始し、
・「   あ」(スペース、スペース、あ)
と打ち込んでみる。出来たソースコードは
Selection.TypeText Text:=" あ"
・・・・これだけ?へえ、簡単。任意の場所を選択してSelection.TypeText Text:="凸"とすれば、自機を表示できるではないか。それではと、場所選択マクロを記録してみる。カーソルを下に5回、右に5回、上に5回、左に5回。
Selection.MoveDown Unit:=wdLine, Count:=5
Selection.MoveRight Unit:=wdCharacter, Count:=5
Selection.MoveUp Unit:=wdLine, Count:=5
Selection.MoveLeft Unit:=wdCharacter, Count:=5

ほほお、相対指定になるようだねえ。今カーソルのある場所からは、これで自在に移動できそう。ちなみに絶対指定はどうするのだろう。普段滅多に使わないジャンプ機能を記録してみよう。20行目までジャンプ、と。出来たコードは
Selection.GoTo What:=wdGoToLine, Which:=wdGoToFirst, Count:=20, Name:=""
実行するとちゃんと20行目が選択される。でも引数が色々あってややこしそうだな。あとは、何が必要?。これで任意の場所に任意の文字を表示させることは出来るし・・・お、そうそう、キャラクターの消去も知っておかなければ。ついでにスペースの挿入もマクロ記録。
Selection.TypeBackspace
Selection.TypeText Text:=" "

簡単簡単。

以上、必要コードまとめ

選択移動(↓→↑←)
Selection.MoveDown Unit:=wdLine, Count:=5
Selection.MoveRight Unit:=wdCharacter, Count:=5
Selection.MoveUp Unit:=wdLine, Count:=5
Selection.MoveLeft Unit:=wdCharacter, Count:=5


行番号指定による移動
Selection.GoTo What:=wdGoToLine, Which:=wdGoToFirst, Count:=20, Name:=""

キャラクター(文字)表示・削除
Selection.TypeText Text:=" あ"
Selection.TypeBackspace
Selection.Delete Unit:=wdCharacter, Count:=1

4 ゲームの仕様を決めてみる

ゲームのフィールドは半角30文字×11行
自機:凸、ミサイル:*、敵:只は最上部より左右に移動する。
プレイヤーは凸を左右に操作しミサイルで只を撃破
ミサイルは同時表示1発、只も同時表示1機
まあ、こんなところか。細かいところは作りながら決めていこう。

5 ゲームのコード的実現手法

これが一番大事なところ。使えるステートメントよりゲーム実現手法を考えてみる。自機の移動は、右移動→自機の左に空白挿入、左移動→自機の左で空白削除、でどうか。敵も左右移動はこれでいけそう。弾は消去しなきゃだめそうだが、消去したらすぐに空白を挿入しといたらいいかも。あらかじめ半角スペースを30文字×11行敷き詰めておいたら、座標指定も簡単そうだ。頭の中でイメージしてみる。うん、いけそう。

6 キー入力取得は?

ここで、ふっと不安がよぎる。自機の操作に必要なキー入力は。APIのGetAsyncKeyStateが使えるだろうか・・・。使えた。少しだけ心配したこの問題も一瞬で解決。じゃあ同期、速度調整に必要なAPIGetTickCountも大丈夫でしょう。

7 では、コーディングの前に、プログラムの流れを整理

いつもはこの過程=コーディングなのですが、今回は慣れないWordマクロということで。

↓GAMESTART
↓全文字消去
↓半角スペース30文字×11行敷き詰め
↓自機表示
↓敵機表示
↓メインループ開始
 ・自機の移動判定、自機表示
 ・ミサイル発射・移動判定、表示
 ・敵移動判定、表示
 ・当たり判定、スコア表示
 ・ゲーム終了判定
↑メインループ終了(繰り返し)
↓ゲーム終了処理

8 いよいよコーディング

おっと、その前にマクロ記録が足りなかった。

ゲーム開始時の処理、全選択、全消去
Selection.WholeStory
Selection.Delete Unit:=wdCharacter, Count:=1

HOMEポジションへ移動
Selection.HomeKey Unit:=wdStory

ということで改めてコーディング開始・・・・

9 そして、完成

文書1シューティングVer1.00のダウンロード
※「Z」キーで強制終了を追加

VBAコードのHTML化には、Bykinさん作「VBAコードHTML変換」を使用させていただいてます。


'************************************************************

'文書1シューティング Ver1.00
'                 2004/6/12  By N.Chikada
'http://www1.plala.or.jp/chikada/
'************************************************************
Option Explicit
Declare Function GetTickCount Lib "kernel32" () As Long     'Windows起動後経過時間取得API
Declare Function GetAsyncKeyState Lib "User32.dll" (ByVal vKey As IntegerAs Long
Declare Function PlaySound Lib "winmm.dll" Alias "PlaySoundA" _
        (
ByVal lpszName As StringByVal hModule As LongByVal dwFlags As LongAs Long
Const VK_LEFT As Long = &H25
Const VK_RIGHT As Long = &H27
Const VK_Z As Long = &H90


Sub GameStart()
Dim i As Integer
Dim Score As Long

Dim z(1 To 3, 1 To 2) As Integer '座標収納用(1:自機2:ミサイル3:敵、1:行2:列)
Dim h As Integer '敵の進行方向(1:右,-1:左,)
h = 1
z(1, 1) = 10 
'自機行
z(1, 2) = 1 
'自機列
z(2, 1) = 0 
'弾行
z(2, 2) = 0 
'弾列
z(3, 1) = 0 
'敵行
z(3, 2) = 2 
'敵列
Dim GameF As Boolean 'ゲーム進行フラグ
GameF = 
True

Dim StartTime(0 To 2) As Long '0:ゲーム開始、1:ループ開始、3:当たり表示

'全選択消去
Selection.HomeKey Unit:=wdStory
Selection.WholeStory
Selection.Delete Unit:=wdCharacter, Count:=1

'半角スペース20文字×10行敷き詰め
For i = 1 To 10
   Selection.TypeText Text:="                              "
   Selection.TypeParagraph  
'改行
Next i

Selection.TypeText Text:="文書1シューティングVer1.00 By N.Chikada"
Selection.MoveRight Unit:=wdCharacter, Count:=30, Extend:=wdExtend
Selection.Font.Bold = wdToggle
Selection.Font.Color = wdColorBlue
Selection.TypeText Text:=" Http://www1.plala.or.jp/chikada/"
Selection.MoveLeft Unit:=wdCharacter, Count:=32, Extend:=wdExtend
ActiveDocument.Hyperlinks.Add Anchor:=Selection.Range, Address:= _
   "http://www1.plala.or.jp/chikada/", SubAddress:=""
Selection.TypeParagraph  
'改行
Selection.TypeText Text:="ゲーム開始はメニューバーのツール>マクロ>マクロ>GameStartを選択>実行"
Selection.TypeParagraph  
'改行
Selection.TypeText Text:="※マクロ無効の場合は、ツール>マクロ>セキュリティを中に設定すると実行できます。"
Selection.TypeParagraph  
'改行

Selection.TypeText Text:="左右キーで自機凸を移動、「只」を狙ってShiftキーでミサイル発射。"
Selection.TypeParagraph  
'改行
Selection.TypeText Text:="30 秒間の命中回数を競う本格派シューティングゲームです。"
Selection.TypeParagraph  
'改行

Selection.WholeStory
Selection.Font.Name = "MS ゴシック"

'敵、自機の配置
Selection.HomeKey Unit:=wdStory
'Selection.MoveDown Unit:=wdLine, Count:=1
Selection.MoveRight Unit:=wdCharacter, Count:=1
Selection.TypeText Text:="只"

Selection.HomeKey Unit:=wdStory
Selection.MoveDown Unit:=wdLine, Count:=10
Selection.TypeText Text:="凸"
Selection.TypeParagraph  
'改行

StartTime(0) = GetTickCount

'メインループ開始
Do While GameF
   StartTime(1) = GetTickCount

   
'  自機の移動判定、自機表示
   Selection.HomeKey Unit:=wdStory
   Selection.MoveDown Unit:=wdLine, Count:=10
   
If GetAsyncKeyState(VK_LEFT) <> 0 Then
     
If z(1, 2) > 1 Then
        z(1, 2) = z(1, 2) - 1
        Selection.Delete Unit:=wdCharacter, Count:=1
     
End If
   
ElseIf GetAsyncKeyState(VK_RIGHT) <> 0 Then
     
If z(1, 2) < 30 Then
       z(1, 2) = z(1, 2) + 1
      Selection.TypeText Text:=" "
      
End If
   
End If

   
' ミサイル発射・移動判定、表示
   
If z(2, 1) = -1 Then '非出現
      
If GetAsyncKeyState(16) <> 0 Then
         z(2, 2) = z(1, 2)
         z(2, 1) = 9
      
End If
   
ElseIf z(2, 1) > 0 Then '上端か移動中
         
'直前の弾を消去
         Selection.HomeKey Unit:=wdStory
         Selection.MoveDown Unit:=wdLine, Count:=z(2, 1)
         Selection.MoveRight Unit:=wdCharacter, Count:=z(2, 2)
         Selection.Delete Unit:=wdCharacter, Count:=1
         Selection.TypeText Text:=" "
         z(2, 1) = z(2, 1) - 1
   
Else
      z(2, 1) = -1
   
End If

   
'弾の表示
   
If z(2, 1) > 0 Then
      Selection.HomeKey Unit:=wdStory
      Selection.MoveDown Unit:=wdLine, Count:=z(2, 1)
      Selection.MoveRight Unit:=wdCharacter, Count:=z(2, 2)
      Selection.TypeText Text:="*"
   
End If

   
'  敵移動判定、表示
   
'折り返しの判定
   
Select Case z(3, 2)
      
Case 1
       h = 1
       
'z(3, 1) = z(3, 1) + 1
      
Case 30
       h = -1
       
'z(3, 1) = z(3, 1) + 1
   
End Select
   z(3, 2) = z(3, 2) + h
   Selection.HomeKey Unit:=wdStory
   Selection.MoveDown Unit:=wdLine, Count:=z(3, 1) - 1
   
If h = -1 Then
        Selection.Delete Unit:=wdCharacter, Count:=1
   
ElseIf h = 1 Then
      Selection.TypeText Text:=" "
   
End If

   
'  当たり判定、スコア表示
   
If z(2, 1) = z(3, 1) And z(2, 2) = z(3, 2) Then
      Score = Score + 1
      StartTime(2) = GetTickCount
      Selection.HomeKey Unit:=wdStory
      Selection.MoveDown Unit:=wdLine, Count:=5
      Selection.MoveRight Unit:=wdCharacter, Count:=13
      Selection.TypeText Text:="HIT!"
      Selection.HomeKey Unit:=wdStory
      
Do Until GetTickCount - StartTime(2) > 500
      
Loop
      Selection.HomeKey Unit:=wdStory
      Selection.MoveDown Unit:=wdLine, Count:=5
      Selection.MoveRight Unit:=wdCharacter, Count:=13
      Selection.Delete Unit:=wdCharacter, Count:=4
   
End If

   
'同期Wait
   
Do While GetTickCount - StartTime(1) < 50
      
'  ゲーム終了判定
      
If GetTickCount - StartTime(0) > 30000 Then
         GameF = 
False
         
Exit Do
      
End If
   
Loop

   
If GetAsyncKeyState(VK_Z) <> 0 Then
      GameF = 
False
   
End If

'メインループ終了 (繰り返し)
Loop

'ゲーム終了処理
MsgBox Score & "発命中"

End Sub


10 作ってみて感想

コード、Selectionだらけですね。カーソルを動かさずに文字列を書き換える方法あるのでしょうか。度に動くカーソルでチカチカしちゃうし。
ちなみに
'同期Wait
Do While GetTickCount - StartTime(1) < 50

Do While GetTickCount - StartTime(1) < 0
とするとノーWaitでワードが爆走します。P4等の高スペックPCだと、感動的な速さが味わえます。

一応書いときますか。
「Wordで動かすことに意義がある」
・・・・・とは今のところ言い切れないなあ。

次回、「Wordの表でセルベーダー」にもご期待下さい(嘘)

2004/6/12 動かす会 近田伸矢