第七回講義 「簡単なゲームを作ろう—前編— 疑似乱数と衝突判定」

今回の講義はここまでの総まとめという事で、「簡単なゲームを作る」という事になっっていますが、まぁ、そんなに難しいのは作りません。極々簡単なもので、「どこかにランダムにキャラが表示され、それを追いかける」というものです。

使う技術は、前回講義で使用した「カーソル移動」をそのまま流用しながら、更に(疑似)乱数というものを導入することにします。


という事で、まずは乱数の実現をしなければなりません。

ところでここでいう乱数というものは、疑似乱数というものであって、JISで定義されているような乱数は実現していません。ですから、値の出現確率にやや偏重があったりする可能性もありますが、極力そのような事のないようにしようと思います。

さて、まず簡単に「一般に言う乱数」というものを考えて見ましょう。

我々が実際にでくわす乱数というのは、ある程度の散乱があり、統一性が見い出しにくければそれだけで「ランダムだ」判断してしまいます。という事は、この事を利用すると、ちょっとした簡単な方法で疑似乱数が実現できます。

まずは、最も簡単なアルゴリズムで実現できるものを示しましょう。


        Proc    QuasiRandomRoutine1     Near

        push    ax bx

                mov     ax,[Word cs:RandomValue]
                mov     bx,ax
                shl     ax,3    ;ax := Random Value * 8
                inc     ax
                mov     [Word cs:RandomValue],ax

        pop     bx ax
        ret

        EndP

RandomValue:
        dw      ?


この方法では「以前の乱数値 × 8 + 1」という演算を行っていますが、値自体は一方的に大きくなっていく方向にありますが、例えば下位1 Byteだけをとってみると、これが実に以外になかなかの乱数値を弾いています。

また、×8としていますが、この値を変更したり、+1を0以外の値にしたりするだけで、更に違った結果が得られます。

ただ欠点としては、この方法ではある程度の回数演算を繰り返すと、次からの値が想像しやすなるという欠点をもっています(線形な演算を元しているので、当然の事ですが)。

ではこの欠点を解消するにはどうすればいいのでしょうか。

ここで電算機の機能を思い返して欲しいのですが、大抵の電算機には「時計」というものが内臓されています。時計の値は連続に見ると単に連続した数列でしかないのですが、最初は3秒後、次は7秒後、6秒後、2秒後・・・と時計を参照する時間自体がまちまちであったとすると、その時の時計の値、特に秒単位の部位などというのは、実によくできた乱数みたいなものとなります。

つまり、この時計の値を利用するという事です。ただし、間違ってもこの時計の値をそのまま利用するのではなく(そのまま利用してもいいのですが、それではあまりにも芸がないものですので)、先程の疑似乱数Routineに利用してやりたいとおもいます。


        Proc    QuasiRandomRoutine2     Near

        push    ax bx

                call    NowTimeGet
                mov     ax,[Word cs:RandomValue]
                mul     [Word cs:NowTime]
                call    NowTimeGet
                add     ax,[Word cs:NowTime]
                mov     [Word cs:RandomValue],ax

        pop     bx ax
        ret

        EndP

        Proc    NowTimeGet              Near

        push    ax bx cx dx

                mov     ax,00200h       ;PC-88VA Only
                int     08ch            ;PC-88VA Only

                mov     ax,02c00h       ;MS-DOS Function
                int     021h            ;MS-DOS Function

                and     dx,00000111100000000b
                xchg    dh,dl
                inc     dx
                mov     [Word cs:NowTime],dx

        pop     dx cx bx ax
        ret

        EndP

RandomValue:
        dw      ?
NowTime:
        dw      ?


やってる事は実に単純ですね。まず時間を読み取り、その時間の秒の1の位を掛算し、さらにもう一回時間を読んで、同じく秒の1の位を足算しています(尚、非零値を必ず得られるようにする為に、秒は+1されています)。こうすれば、かなり乱数に近い値が得られるようになっている事と思います。

(注:NowTimeGet Procedureに於いて、PC-88VA Seriesをお使いの場合は、MS-DOS FunctionとCommentを打っている行を削除してください。また、VA以外の機種ではPC-88VA OnlyとCommentを打っている行を削除してください(尚、MS-DOS(或いは互換環境)上で動作させている事が前提です。そうでない場合は各機種の持つ時間の読み込み方法を利用してください))


さてこれで、殆ど乱数が実現できたに等しいのですが、まだまだ飽きたらないという場合には更に次の方法を示す事にしましょう。


        Proc    QuasiRandomRoutine3     Near

        push    ax bx

                mov     ax,[Word cs:RandomValue]
                mov     bx,ax
                shl     bx,8    ;BX := AX * 259
                add     bx,ax
                shl     ax,1
                add     bx,ax
                mov     [Word cs:RandomValue],bx

        pop     bx ax
        ret

        EndP

RadomValue:
        dw      ?


この方法では「以前の値 × 259」という演算を繰り返すだけです。実に簡単なもので、以前私が作ったゲームでも実際に採用した方法でもあります。

このように疑似でいいとあれば、乱数は実に簡単に実現する事ができます。


(注:これら疑似乱数は、以前本講義中でも紹介しました

   「8086マシン語秘伝の書」 日高 徹/青山 学 共著  啓学出版

   という書籍の86頁から99頁までの内容を参考に作成されています)


さて、乱数の実現ができましたので、次にこれを利用したいと思います。

今回目標では「キャラをランダムに表示する」という事に意義がありますので、これを実現しなければなりません。

という事でこの疑似乱数関数を実際に使用したものを示してみましょう。


        Proc    ObjectCharacterSet      Near

        push    ax bx cx dx

ObjectCharacterXLocation:       ;X軸の設定
                mov     al,79
                xor     bx,bx
                call    QuasiRandomRoutine
                mov     bl,[Byte cs:RandomValue]
ObjectCharacterXLocationLoop:
                cmp     bl,al   ;X軸がはみ出していないか
                jbe     ObjectCharacterYLocation
                sub     bl,al   ;はみ出さなくなるまで引算
                jmp     Short ObjectCharacterXLocationLoop

ObjectCharacterYLocation:       ;Y軸の設定
                mov     al,23
                xor     cx,cx
                call    QuasiRandomRoutine
                mov     cl,[Byte cs:RandomValue]
ObjectCharacterYLocationLoop:
                cmp     cl,al   ;Y軸がはみ出していないか
                jbe     ObjectCharacterPrint
                sub     cl,al   ;はみ出さなくなるまで引算
                jmp     Short ObjectCharacterYLocationLoop

ObjectCharacterPrint:
                mov     [Word cs:ObjectCharacterPoint + 0],bx
                mov     [Word cs:ObjectCharacterPoint + 2],cx
                mov     dl,'O'
                call    CharacterPrint

        pop     dx cx bx ax
        ret

        EndP


;Call
;  [Word cs:RandomValue] : Random Seed
;
;Return
;  [Word cs:RandomValue] : 求めたRandom値
        Proc    QuasiRandomRoutine      Near

        push    ax bx

                mov     ax,[Word cs:RandomValue]
                mov     bx,ax
                shl     bx,8    ;BX := AX * 259
                add     bx,ax
                shl     ax,1
                add     bx,ax
                mov     [Word cs:RandomValue],bx

        pop     bx ax
        ret

        EndP


;Call
;  BX : X軸
;  CX : Y軸
;  DL : 表示する文字
        Proc    CharacterPrint  Near

	push    bx cx di es

                shl     bx,1    ;BX := BX * 2
                mov     di,bx
                shl     cx,5    ;CX := CX * (80 * 2)
                add     di,cx
                shl     cx,2
                add     di,cx

                mov     bx,0a000h
                mov     es,bx
                mov     [Byte es:di],dl

        pop     es di cx bx
        ret

        EndP


ObjectCharacterPoint:
        dw      ?               ;X
        dw      ?               ;Y
RadomValue:
        dw      ?


この例では3つ目に示した疑似乱数を使用しています。

さてここでやっている事は実に簡単なものです。単に乱数を読み出して、その値が範囲に納まるように加工(引算)し、表示するというだけです。

このようにすれば、実に簡単に「ランダムな位置に表示する」という事で実現できたわけですね。


では、最後に衝突判定を付け足し、ゲームとして実現させましょう。

まず衝突判定というものですが、これ単に「座標の重なり」を見ればいいだけです。

つまり、自キャラの位置と目的の位置とが一致すれば「衝突」と判断し、不一致ならば「非衝突」というわけになるのですが、こういった判断方法は実によく多用されているものだと思います。

ちなみに、この方法ではCharacter数が多くなると時間がかかるようになってしまうという欠点がありますが、次回講義で取り上げる予定の仮想画面を利用して衝突を判断するという技法もありますが、ここでは取り上げません(内容は次回ということで)。

さて、では例を示しましょう。


;For PC-88VA Sample Program

;--------For Assembler Option
        Ideal
        P186
        Model   Small
        Stack   00200h




;--------Code Segment Start
CodeSeg




;--------Main Process

;........Main Process - Initialize
Initialize:
        call    TextVRAMInitialize
        call    RandomizeRoutine
        call    ObjectCharacterSet
        call    KeyboardInitialize

        mov     bx,[Word cs:CharacterPoint + 0]
        mov     cx,[Word cs:CharacterPoint + 2]
        mov     dl,'A'
        call    CharacterPrint

;........Main Process - MainRoutine
MainRoutine:
        call    VSYNCWait
        call    MyCharacterMove
        test    al,000010000b
        jnz     ReturnSystem
        call    CollisionCheck
        or      ax,ax
        jz      MainRoutine
        call    ObjectCharacterSet
        jmp     Short MainRoutine

;........Main Process - ReturnSystem
ReturnSystem:
        mov     ax,04c00h
        int     021h




;--------Other Procedures

;........Other Procedures - VSYNCWait
;
;  Call
;    None
;
;  Return
;    None
;
        Proc    VSYNCWait       Near

        push	ax

VSYNCWait1:
                in      al,040h
                test    al,000100000b
                jz      VSYNCWait1
VSYNCWait2:
                in      al,040h
                test    al,000100000b
                jnz     VSYNCWait2

        pop     ax
        ret

        EndP

;........Other Procedures - CollisionCheck
;
;  Call
;    [DWord cs:CharacterPoint]       : 自キャラ現在位置
;    [DWord cs:ObjectCharacterPoint] : 目的現在位置
;
;  Return
;    AX : 衝突していればFFFFh、していなければ0000h
;
        Proc    CollisionCheck  Near

        push    bx cx

                xor     ax,ax
                mov     bx,[Word cs:CharacterPoint + 0]
                mov     cx,[Word cs:CharacterPoint + 2]
                cmp     bx,[Word cs:ObjectCharacterPoint + 0]
                jnz     CollisionCheckEnd
                cmp     cx,[Word cs:ObjectCharacterPoint + 2]
                jnz     CollisionCheckEnd
                not     ax

CollisionCheckEnd:
        pop     cx bx
        ret

        EndP




;--------Character Set

;........Character Set - MyCharacterMove
;
;  Call
;    [Word cs:CharatcerPoint + 0] : 自キャラX軸
;    [Word cs:CharatcerPoint + 2] : 自キャラY軸
;
;  Return
;    AL                           : 押されたKeyの内容
;    [Word cs:CharatcerPoint + 0] : 自キャラX軸
;    [Word cs:CharatcerPoint + 2] : 自キャラY軸
;
        Proc    MyCharacterMove Near

        push    bx cx dx

                call    KeyInputCheck
                or      al,al
                jz      MyCharacterMoveEnd
                test    al,000010000b
                jnz     MyCharacterMoveEnd

MyCharacterMoveCheckStart:
                mov     bx,[Word cs:CharacterPoint + 0]
                mov     cx,[Word cs:CharacterPoint + 2]
                mov     dl,' '
                call    CharacterPrint
MyCharacterMoveUpCheck:
                test    al,000001000b
                jz      MyCharacterMoveDownCheck
                or      cx,cx
                jz      MyCharacterMoveDownCheck
                dec     cx
MyCharacterMoveDownCheck:
                test    al,000000100b
                jz      MyCharacterMoveLeftCheck
                cmp     cx,23
                jae     MyCharacterMoveLeftCheck
                inc     cx
MyCharacterMoveLeftCheck:
                test    al,000000010b
                jz      MyCharacterMoveRightCheck
                or      bx,bx
                jz      MyCharacterMoveRightCheck
                dec     bx
MyCharacterMoveRightCheck:
                test    al,000000001b
                jz      MyCharacterMoveEndFront
                cmp     bx,79
                jae     MyCharacterMoveEndFront
                inc     bx
MyCharacterMoveEndFront:
                mov     dl,'A'
                call    CharacterPrint
                mov     [Word cs:CharacterPoint + 0],bx
                mov     [Word cs:CharacterPoint + 2],cx

MyCharacterMoveEnd:
        pop     dx cx bx
        ret

        EndP

;........Character Set - ObjectCharacterSet
;
;  Call
;    None
;
;  Return
;    [Word cs:ObjectCharacterPoint + 0] : 目的のX軸
;    [Word cs:ObjectCharacterPoint + 2] : 目的のY軸
;
        Proc    ObjectCharacterSet              Near

        push    ax bx cx dx

ObjectCharacterXLocation:	;X軸の設定
                mov     al,79
                xor     bx,bx
                call    QuasiRandomRoutine
                mov     bl,[Byte cs:RandomValue]
ObjectCharacterXLocationLoop:
                cmp     bl,al   ;X軸がはみ出していないか
                jbe     ObjectCharacterYLocation
                sub     bl,al   ;はみ出さなくなるまで引算
                jmp     Short ObjectCharacterXLocationLoop

ObjectCharacterYLocation:       ;Y軸の設定
                mov     al,23
                xor     cx,cx
                call    QuasiRandomRoutine
                mov     cl,[Byte cs:RandomValue]
ObjectCharacterYLocationLoop:
                cmp     cl,al   ;Y軸がはみ出していないか
                jbe     ObjectCharacterPrint
                sub     cl,al   ;はみ出さなくなるまで引算
                jmp     Short ObjectCharacterYLocationLoop

ObjectCharacterPrint:
                mov     [Word cs:ObjectCharacterPoint + 0],bx
                mov     [Word cs:ObjectCharacterPoint + 2],cx
                mov     dl,'O'
                call    CharacterPrint

        pop     dx cx bx ax
        ret

        EndP




;--------Random Routines

;........Random Routines - RandomizeRoutine
;
;  Call
;    None
;
;  Return
;    [Word cs:RandomValue] : Random Seed
;
        Proc    RandomizeRoutine        Near

        push    ax

                call    NowTimeGet
                mov     ax,[Word cs:NowTime]
                mov     [Word cs:RandomValue],ax

        pop     ax
        ret

        EndP

;........Random Routines - QuasiRandomRoutine
;
;  Call
;    [Word cs:RandomValue] : Random Seed
;
;  Return
;    [Word cs:RandomValue] : 求めたRandom値
;
        Proc    QuasiRandomRoutine      Near

        push    ax bx

                mov     ax,[Word cs:RandomValue]
                mov     bx,ax
                shl     bx,8    ;BX := AX * 259
                add     bx,ax
                shl     ax,1
                add     bx,ax
                mov     [Word cs:RandomValue],bx

        pop     bx ax
        ret

        EndP

;........Random Routines - NowTimeGet
;
;  Call
;    None
;
;  Return
;    [Word cs:NowTime] : 現在時刻(秒の1の位)
;
        Proc    NowTimeGetNear  Near

        push    ax bx cx dx

                mov     ax,00200h
                int     08ch

                and     dx,00000111100000000b
                xchg    dh,dl
                inc     dx
                mov     [Word cs:NowTime],dx

        pop     dx cx bx ax
        ret

        EndP




;--------Keyboard Routines

;........Keyboard Routines - KeyboardInitialize
;
;  Call
;    None
;
;  Return
;    None
;
        Proc    KeyboardInitialize      Near

        push    ax

                mov     ax,00500h
                int     082h

        pop     ax
        ret

        EndP

;........Keyboard Routines - KeyInputCheck
;
;  Call
;    None
;
;  Return
;    AL : Key Push Flag
;         None - None - None - Stop = Up - Down - Left - Right
;
        Proc    KeyInputCheck   Near

        pushf
        push    bx cx ds

                cld
                mov     ax,cs
                mov     ds,ax
                mov     si,Offset InputKeyTable
                xor     bx,bx
                mov     cx,5
KeyInputCheckLoop:
                push    cx
                lodsb
                mov     ah,00dh
                int     082h
                lodsb
                mov     cl,al
                shr     ah,cl
                rcl     bl,1
                pop     cx
                loop    KeyInputCheckLoop
                mov     ax,bx

        pop     ds cx bx
        popf
        ret

        EndP

;........Keyboard Routines - KeyBufferClear
;
;  Call
;    None
;
;  Return
;    None
;
        Proc    KeyBufferClear  Near

        push    ax

                mov     ax,00400h
                int     082h

        pop     ax
        ret

        EndP




;--------Text Routines

;........Text Routines - TextVRAMinitialize
;
;  Call
;    None
;
;  Return
;    None
;
        Proc    TextVRAMInitialize      Near

        push    ax dx

                mov     ax,01702h
                int     083h
                mov     dx,00154h       ;VRAM Bank Change (T-VRAM)
                mov     al,041h
                out     dx,al

        pop     dx ax
        ret

        EndP

;........Text Routines - Character Print
;
;  Call
;    BX : X軸
;    CX : Y軸
;    DL : 表示する文字
;
;  Return
;    None
;
        Proc    CharacterPrint  Near

        push    bx cx di es

                shl     bx,1    ;BX := BX * 2
                mov     di,bx
                shl     cx,5    ;CX := CX * (80 * 2)
                add     di,cx
                shl     cx,2
                add     di,cx

                mov     bx,0a000h
                mov     es,bx
                mov     [Byte es:di],dl

        pop     es di cx bx
        ret

        EndP




;--------Datas

;........Datas - CharacterPoint
CharacterPoint:
        dw      40              ;X
        dw      12              ;Y

;........Datas - ObjectCharacterPoint
ObjectCharacterPoint:
        dw      ?               ;X
        dw      ?               ;Y

;........Datas - RandomValue
RandomValue:
        dw      ?

;........Datas - NowTime
NowTime:
        dw      ?

;........Datas - InputKeyTable
InputKeyTable:
        db      009h,1          ;Stop
        db      008h,2          ;Up
        db      00ah,2          ;Down
        db      00ah,3          ;Left
        db      008h,3          ;Right




;--------End of Source

        End


;For PC-9800 Sample Program

;--------For Assembler Option
	Ideal
	P186
	Model   Small
	Stack   00200h




;--------Code Segment Start
CodeSeg




;--------Main Process

;........Main Process - Initialize
Initialize:
        call    TextVRAMInitialize
        call    RandomizeRoutine
        call    ObjectCharacterSet
        call    KeyboardInitialize

        mov     bx,[Word cs:CharacterPoint + 0]
        mov     cx,[Word cs:CharacterPoint + 2]
        mov     dl,'A'
        call    CharacterPrint

;........Main Process - MainRoutine
MainRoutine:
        call    VSYNCWait
        call    MyCharacterMove
        test    al,000010000b
        jnz     ReturnSystem
        call    CollisionCheck
        or      ax,ax
        jz      MainRoutine
        call    ObjectCharacterSet
        jmp     Short MainRoutine

;........Main Process - ReturnSystem
ReturnSystem:
        call    KeyBufferClear
        mov     ax,04c00h
        int     021h




;--------Other Procedures

;........Other Procedures - VSYNCWait
;
;  Call
;    None
;
;  Return
;    None
;	
        Proc    VSYNCWait       Near

        push    ax

VSYNCWait1:
                in      al,0a0h
                test    al,000100000b
                jz      VSYNCWait1
VSYNCWait2:
                in      al,0a0h
                test    al,000100000b
                jnz     VSYNCWait2

        pop     ax
        ret

        EndP

;........Other Procedures - CollisionCheck
;
;  Call
;    [DWord cs:CharacterPoint]       : 自キャラ現在位置
;    [DWord cs:ObjectCharacterPoint] : 目的現在位置
;
;  Return
;    AX : 衝突していればFFFFh、していなければ0000h
;
        Proc    CollisionCheck  Near

        push    bx cx

                xor     ax,ax
                mov     bx,[Word cs:CharacterPoint + 0]
                mov     cx,[Word cs:CharacterPoint + 2]
                cmp     bx,[Word cs:ObjectCharacterPoint + 0]
                jnz     CollisionCheckEnd
                cmp     cx,[Word cs:ObjectCharacterPoint + 2]
                jnz     CollisionCheckEnd
                not     ax

CollisionCheckEnd:
        pop     cx bx
        ret

        EndP




;--------Character Set

;........Character Set - MyCharacterMove
;
;  Call
;    [Word cs:CharatcerPoint + 0] : 自キャラX軸
;    [Word cs:CharatcerPoint + 2] : 自キャラY軸
;
;  Return
;    AL                           : 押されたKeyの内容
;    [Word cs:CharatcerPoint + 0] : 自キャラX軸
;    [Word cs:CharatcerPoint + 2] : 自キャラY軸
;
        Proc    MyCharacterMove Near

        push    bx cx dx

                call    KeyInputCheck
                or      al,al
                jz      MyCharacterMoveEnd
                test    al,000010000b
                jnz     MyCharacterMoveEnd

MyCharacterMoveCheckStart:
                mov     bx,[Word cs:CharacterPoint + 0]
                mov     cx,[Word cs:CharacterPoint + 2]
                mov     dl,' '
                call    CharacterPrint
MyCharacterMoveUpCheck:
                test    al,000001000b
                jz      MyCharacterMoveDownCheck
                or      cx,cx
                jz      MyCharacterMoveDownCheck
                dec     cx
MyCharacterMoveDownCheck:
                test    al,000000100b
                jz      MyCharacterMoveLeftCheck
                cmp     cx,23
                jae     MyCharacterMoveLeftCheck
                inc     cx
MyCharacterMoveLeftCheck:
                test    al,000000010b
                jz      MyCharacterMoveRightCheck
                or      bx,bx
                jz      MyCharacterMoveRightCheck
                dec     bx
MyCharacterMoveRightCheck:
                test    al,000000001b
                jz      MyCharacterMoveEndFront
                cmp     bx,79
                jae     MyCharacterMoveEndFront
                inc     bx
MyCharacterMoveEndFront:
                mov     dl,'A'
                call    CharacterPrint
                mov     [Word cs:CharacterPoint + 0],bx
                mov     [Word cs:CharacterPoint + 2],cx

MyCharacterMoveEnd:
        pop     dx cx bx
        ret

        EndP

;........Character Set - ObjectCharacterSet
;
;  Call
;    None
;
;  Return
;    [Word cs:ObjectCharacterPoint + 0] : 目的のX軸
;    [Word cs:ObjectCharacterPoint + 2] : 目的のY軸
;
        Proc    ObjectCharacterSet      Near
        push    ax bx cx dx

ObjectCharacterXLocation:       ;X軸の設定
                mov     al,79
                xor     bx,bx
                call    QuasiRandomRoutine
                mov     bl,[Byte cs:RandomValue]
ObjectCharacterXLocationLoop:
                cmp     bl,al   ;X軸がはみ出していないか
                jbe     ObjectCharacterYLocation
                sub     bl,al   ;はみ出さなくなるまで引算
                jmp     Short ObjectCharacterXLocationLoop

ObjectCharacterYLocation:       ;Y軸の設定
                mov     al,23
                xor     cx,cx
                call    QuasiRandomRoutine
                mov     cl,[Byte cs:RandomValue]
ObjectCharacterYLocationLoop:
                cmp     cl,al   ;Y軸がはみ出していないか
                jbe     ObjectCharacterPrint
                sub     cl,al   ;はみ出さなくなるまで引算
                jmp     Short ObjectCharacterYLocationLoop

ObjectCharacterPrint:
                mov     [Word cs:ObjectCharacterPoint + 0],bx
                mov     [Word cs:ObjectCharacterPoint + 2],cx
                mov     dl,'O'
                call    CharacterPrint

        pop     dx cx bx ax
        ret

        EndP




;--------Random Routines

;........Random Routines - RandomizeRoutine
;
;  Call
;    None
;
;  Return
;    [Word cs:RandomValue] : Random Seed
;
        Proc    RandomizeRoutine        Near

        push    ax

                call    NowTimeGet
                mov     ax,[Word cs:NowTime]
                mov     [Word cs:RandomValue],ax

        pop     ax
        ret

        EndP

;........Random Routines - QuasiRandomRoutine
;
;  Call
;    [Word cs:RandomValue] : Random Seed
;
;  Return
;    [Word cs:RandomValue] : 求めたRandom値
;
        Proc    QuasiRandomRoutine      Near

        push    ax bx

                mov     ax,[Word cs:RandomValue]
                mov     bx,ax
                shl     bx,8    ;BX := AX * 259
                add     bx,ax
                shl     ax,1
                add     bx,ax
                mov     [Word cs:RandomValue],bx

        pop     bx ax
        ret

        EndP

;........Random Routines - NowTimeGet
;
;  Call
;    None
;
;  Return
;    [Word cs:NowTime] : 現在時刻(秒の1の位)
;
        Proc    NowTimeGet      Near

        push    ax bx cx dx

                mov     ax,02c00h
                int     021h

                and     dx,00000111100000000b
                xchg    dh,dl
                inc     dx
                mov     [Word cs:NowTime],dx

        pop     dx cx bx ax
        ret

        EndP




;--------Keyboard Routines

;........Keyboard Routines - KeyboardInitialize
;
;  Call
;    None
;
;  Return
;    None
;
        Proc    KeyboardInitialize      Near

        push    ax

                mov     ax,00300h
                int     018h

        pop     ax
        ret

        EndP

;……..Keyboard Routines - KeyInputCheck
;
;  Call
;    None
;
;  Return
;    AL : Key Push Flag
;         None - None - None - Stop = Up - Down - Left - Right
;
        Proc    KeyInputCheck   Near

        pushf
        push    bx cx ds

                cld
                mov     ax,cs
                mov     ds,ax
                mov     si,Offset InputKeyTable
                xor     bx,bx
                mov     cx,5
KeyInputCheckLoop:
                push    cx
                lodsb
                mov     ah,004h
                int     018h
                lodsb
                mov     cl,al
                shr     ah,cl
                rcl     bl,1
                pop     cx
                loop    KeyInputCheckLoop
                mov     ax,bx

        pop     ds cx bx
        popf
        ret

        EndP

;........Keyboard Routines - KeyBufferClear
;
;  Call
;    None
;
;  Return
;    None
;
        Proc    KeyBufferClear  Near

        push    ax bx

KeyBufferClearLoop:
                mov     ax,00100h
                int     018h
                or      bh,bh
                jz      KeyBufferClearEnd
                xor     ax,ax
                int     018h
                jmp     Short KeyBufferClearLoop

KeyBufferClearEnd:
        pop     bx ax
        ret

        EndP




;--------Text Routines

;........Text Routines - TextVRAMinitialize
;
;  Call
;    None
;
;  Return
;    None
;
        Proc    TextVRAMInitialize      Near

        pushf
        push    ax cx di es

                cld
                mov     ax,0a000h
                mov     es,ax
                xor     di,di
                mov     ax,00020h
                mov     cx,80 * 24
                rep     stosw

        pop     es di cx ax
        popf
        ret

        EndP

;........Text Routines - Character Print
;
;  Call
;    BX : X軸
;    CX : Y軸
;    DL : 表示する文字
;
;  Return
;    None
;
        Proc    CharacterPrint  Near

        push    bx cx di es

                shl	bx,1    ;BX := BX * 2
                mov	di,bx
                shl	cx,5    ;CX := CX * (80 * 2)
                add	di,cx
                shl	cx,2
                add	di,cx

                mov	bx,0a000h
                mov	es,bx
                mov	[Byte es:di],dl

        pop     es di cx bx
        ret

        EndP




;--------Datas

;........Datas - CharacterPoint
CharacterPoint:
        dw      40              ;X
        dw      12              ;Y

;........Datas - ObjectCharacterPoint
ObjectCharacterPoint:
        dw      ?                ;X
        dw      ?                ;Y

;........Datas - RandomValue
RandomValue:
        dw      ?

;........Datas - NowTime
NowTime:
        dw      ?

;........Datas - InputKeyTable
InputKeyTable:
        db      00ch,1          ;Stop
        db      007h,3          ;Up
        db      007h,6          ;Down
        db      007h,4          ;Left
        db      007h,5          ;Right




;--------End of Source

        End


このゲームでは自キャラは「A」、目標は「O」で表示されています。

Cursor Keyで移動し、Stop Keyで終了になります。

やってる事は今までやった事全てを用いているだけで、追加した内容はありませんね。実に単純な設計だと思います。


というわけで、ここまでで目標としいた「単純なゲームを作る」という事は実現できました。

しかし、今回の内容ではあまりに単純すぎましたので、先程もいいましたように次は「仮想画面の実現」を取り上げ、更に実際に実用できる直前の段階までつきとめようと思います。

というわけで次回講義は「簡単なゲームを作ろう—中編— 仮想画面の実現」です。

ちなみに、「中編」なのはあともう1つ最後に付け足しとして「—後編— 味付け」ということで、効果音をつけてみたり、点数をつけてみたり、時間制限を考えみたり等々、更にゲームらしくする事が目的です(尚、Graphicは使用せずTextのみで行います)。

では次回をお楽しみに。


Back


手元に実機も実行環境もありませんのでサポート不可です。

文章は執筆当時のものをそのまま掲載していますので、時代・時節と合わない表現が含まれています。

また、一部に半角カナ文字が使用されています。