記事一覧

野球ゲームをつくる ~ 5.バッティングルーレットの作成

バッティングルーレットを作ります。

数字が0から9に回るのなら簡単ですが、
野球ゲーム用のルーレットなので、
単純にアウト、ヒット、ホームランの出目を循環させるわけにはいきません。
なぜなら、ヒットの確率が高くなり、ゲーム性が失われてしまいます。

そこで、下図*1のようなルーレットをつくります。

ファイル 100-1.jpg

このようにすれば、ヒットの確率を調整することができます。

では、これをどのようにプログラムするのかというと、
クロックのタイミング*2で、アウト→フォアボール→アウト→アウト→アウト...
と状態を変化させ、ストップがかかったタイミングで出目を確定させればよいのです。

この状態を変化させるには、ステートマシンを使用します。

今回、ノーマルモード*3とチャレンジモード*4用にステートマシンを2つ作って、ヒットの確率を変えてみます。

また、目押し用*5にホームランのタイミングで信号(ASSIST<= '1')が出るようにします。

batting_roullet.vhd

-- Batting Roullet
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.STD_LOGIC_UNSIGNED.ALL;

entity BATTING_ROULLET is
    port(
        CLK         : in  std_logic;
        RESET       : in  std_logic;
        STARTSTOP   : in  std_logic;
        MODE        : in  std_logic;
        BATTING     : out std_logic_vector(2 downto 0);
        ASSIST      : out std_logic
    );
end BATTING_ROULLET;

architecture RTL of BATTING_ROULLET is

type STATE_TYPE is (
    ONEBASE0,ONEBASE1,ONEBASE2,ONEBASE3,ONEBASE4,
    TWOBASE0,TWOBASE1,
    THREEBASE0,THREEBASE1,
    HOMERUN0,HOMERUN1,HOMERUN2,
    FOUR0,FOUR1,FOUR2,FOUR3,FOUR4,
    OUT0,OUT1,OUT2,OUT3,OUT4,OUT5,OUT6,OUT7,OUT8,OUT9,
    OUT10,OUT11,OUT12,OUT13,OUT14,OUT15,OUT16,OUT17,OUT18,OUT19,
    OUT20,OUT21,OUT22,OUT23,
    CHANGE
    );

signal STATE    : STATE_TYPE;

begin
    
    process(CLK,RESET)
    begin
        if (RESET = '0') then
            STATE <= CHANGE;
        elsif(CLK'event and CLK = '0') then
            if (STARTSTOP = '1') then
                if (MODE = '0') then
                    case(STATE) is        -- Normal MODE
                        when CHANGE      => STATE <= OUT0;
                        when OUT0        => STATE <= FOUR0;
                        when FOUR0       => STATE <= OUT1;
                        when OUT1        => STATE <= TWOBASE0;
                        when TWOBASE0    => STATE <= OUT2;
                        when OUT2        => STATE <= OUT3;
                        when OUT3        => STATE <= OUT4;
                        when OUT4        => STATE <= THREEBASE0;
                        when THREEBASE0  => STATE <= OUT5;
                        when OUT5        => STATE <= ONEBASE0;
                        when ONEBASE0    => STATE <= OUT6;
                        when OUT6        => STATE <= FOUR1;
                        when FOUR1       => STATE <= OUT7;
                        when OUT7        => STATE <= ONEBASE1;
                        when ONEBASE1    => STATE <= OUT8;
                        when OUT8        => STATE <= FOUR2;
                        when FOUR2       => STATE <= OUT9;
                        when OUT9        => STATE <= ONEBASE2;
                        when ONEBASE2    => STATE <= OUT10;
                        when OUT10       => STATE <= TWOBASE1;
                        when TWOBASE1    => STATE <= OUT11;
                        when OUT11       => STATE <= OUT12;
                        when OUT12       => STATE <= OUT13;
                        when OUT13       => STATE <= HOMERUN0;
                        when HOMERUN0    => STATE <= OUT14;
                        when OUT14       => STATE <= FOUR3;
                        when FOUR3       => STATE <= OUT15;
                        when OUT15       => STATE <= ONEBASE3;
                        when ONEBASE3    => STATE <= OUT16;
                        when OUT16       => STATE <= FOUR4;
                        when FOUR4       => STATE <= OUT17;
                        when OUT17       => STATE <= ONEBASE4;
                        when ONEBASE4    => STATE <= OUT0;
                        when others      => STATE <= CHANGE;
                    end case;
                else
                    case(STATE) is        -- Challenge MODE
                        when CHANGE      => STATE <= OUT0;
                        when OUT0        => STATE <= OUT1;
                        when OUT1        => STATE <= OUT2;
                        when OUT2        => STATE <= HOMERUN0;
                        when HOMERUN0    => STATE <= OUT3;
                        when OUT3        => STATE <= OUT4;
                        when OUT4        => STATE <= ONEBASE0;
                        when ONEBASE0    => STATE <= OUT5;
                        when OUT5        => STATE <= OUT6;
                        when OUT6        => STATE <= TWOBASE0;
                        when TWOBASE0    => STATE <= OUT7;
                        when OUT7        => STATE <= OUT8;
                        when OUT8        => STATE <= OUT9;
                        when OUT9        => STATE <= THREEBASE0;
                        when THREEBASE0  => STATE <= OUT10;
                        when OUT10       => STATE <= OUT11;
                        when OUT11       => STATE <= OUT12;
                        when OUT12       => STATE <= OUT13;
                        when OUT13       => STATE <= OUT14;
                        when OUT14       => STATE <= HOMERUN1;
                        when HOMERUN1    => STATE <= OUT15;
                        when OUT15       => STATE <= OUT16;
                        when OUT16       => STATE <= FOUR0;
                        when FOUR0       => STATE <= OUT17;
                        when OUT17       => STATE <= OUT18;
                        when OUT18       => STATE <= HOMERUN2;
                        when HOMERUN2    => STATE <= OUT19;
                        when OUT19       => STATE <= OUT20;
                        when OUT20       => STATE <= OUT21;                                                when OUT21       => STATE <= THREEBASE1;
                        when THREEBASE1  => STATE <= OUT22;                                                when OUT22       => STATE <= OUT23;                                                when OUT23       => STATE <= OUT0;
                        when others      => STATE <= CHANGE;
                    end case;
                end if;
            end if;
        end if;
    end process;
    
    process(STATE)
    begin
        case(STATE) is
            when CHANGE        =>
                BATTING <= "110";
                ASSIST <= '0';
            when OUT0        =>
                BATTING <= "000";
                ASSIST <= '0';
            when OUT1        =>
                BATTING <= "000";
                ASSIST <= '0';
            when OUT2        =>
                BATTING <= "000";
                ASSIST <= '0';
            when OUT3        =>
                BATTING <= "000";
                ASSIST <= '0';
            when OUT4        =>
                BATTING <= "000";
                ASSIST <= '0';
            when OUT5        =>
                BATTING <= "000";
                ASSIST <= '0';
            when OUT6        =>
                BATTING <= "000";
                ASSIST <= '0';
            when OUT7        =>
                BATTING <= "000";
                ASSIST <= '0';
            when OUT8        =>
                BATTING <= "000";
                ASSIST <= '0';
            when OUT9        =>
                BATTING <= "000";
                ASSIST <= '0';
            when OUT10        =>
                BATTING <= "000";
                ASSIST <= '0';
            when OUT11        =>
                BATTING <= "000";
                ASSIST <= '0';
            when OUT12        =>
                BATTING <= "000";
                ASSIST <= '0';
            when OUT13        =>
                BATTING <= "000";
                ASSIST <= '0';
            when OUT14        =>
                BATTING <= "000";
                ASSIST <= '0';
            when OUT15        =>
                BATTING <= "000";
                ASSIST <= '0';
            when OUT16         =>
                BATTING <= "000";
                ASSIST <= '0';
            when OUT17        =>
                BATTING <= "000";
                ASSIST <= '0';
            when OUT18        =>
                BATTING <= "000";
                ASSIST <= '0';
            when OUT19        =>
                BATTING <= "000";
                ASSIST <= '0';
            when OUT20        =>
                BATTING <= "000";
                ASSIST <= '0';
            when OUT21        =>
                BATTING <= "000";
                ASSIST <= '0';
            when OUT22        =>
                BATTING <= "000";
                ASSIST <= '0';
            when OUT23        =>
                BATTING <= "000";
                ASSIST <= '0';
            when ONEBASE0    =>
                BATTING <= "001";
                ASSIST <= '0';
            when ONEBASE1    =>
                BATTING <= "001";
                ASSIST <= '0';
            when ONEBASE2    =>
                BATTING <= "001";
                ASSIST <= '0';
            when ONEBASE3    =>
                BATTING <= "001";
                ASSIST <= '0';
            when ONEBASE4    =>
                BATTING <= "001";
                ASSIST <= '0';
            when TWOBASE0    =>
                BATTING <= "010";
                ASSIST <= '0';
            when TWOBASE1    =>
                BATTING <= "010";
                ASSIST <= '0';
            when THREEBASE0 =>
                BATTING <= "011";
                ASSIST <= '0';
            when THREEBASE1    =>
                BATTING <= "011";
                ASSIST <= '0';
            when HOMERUN0    =>
                BATTING <= "100";
                ASSIST <= '1';
            when HOMERUN1    =>
                BATTING <= "100";
                ASSIST <= '1';
            when HOMERUN2    =>
                BATTING <= "100";
                ASSIST <= '1';
            when FOUR0        =>
                BATTING <= "101";
                ASSIST <= '0';
            when FOUR1        => 
                BATTING <= "101";
                ASSIST <= '0';
            when FOUR2        =>
                BATTING <= "101";
                ASSIST <= '0';
            when FOUR3        =>
                BATTING <= "101";
                ASSIST <= '0';
            when FOUR4        =>
                BATTING <= "101";
                ASSIST <= '0';
            when others        =>
                BATTING <= "000";
                ASSIST <= '0';
        end case;
    end process;
    
end RTL;

*1:何で作図したらよいかわからなかったので、大げさにもCADで作図しました。
*2:ということは、クロックの速度を変えれば、ルーレットが回る速度が変わります。
*3:ヒットの確率は高いが、シングルヒットが多い
*4:ヒットの確率は低いが、出れば長打になる
*5:ズル用とも

野球ゲームをつくる ~ 4.バッティングルーレットの表示

ルーレットを回し、出目によって、アウトやヒットを決めます。

出目:内容
0:アウト
1:1塁打
2:2塁打
3:3塁打
H:ホームラン
F:フォアボール

また、チェンジも表示する必要があるので、
-:チェンジ
とします。

負論理なので、LEDの出力を反転しています。*1

batting_decorder.vhd

entity BATTING_DECODER is
    port(
        RESET   : in  std_logic;
        COUNTER : in  std_logic_vector(2 downto 0);
        LED     : out std_logic_vector(7 downto 1)
    );
end BATTING_DECODER;

architecture RTL of BATTING_DECODER is
signal LED_TMP  : std_logic_vector(7 downto 1);

begin

    process(RESET,COUNTER)
    begin
        if (RESET = '0') then
            LED_TMP <= "0000000";
        else
            case COUNTER is
                when "000" => LED_TMP <= "1111110"; -- 0
                when "001" => LED_TMP <= "0110000"; -- 1
                when "010" => LED_TMP <= "1101101"; -- 2
                when "011" => LED_TMP <= "1111001"; -- 3
                when "100" => LED_TMP <= "0110111"; -- H
                when "101" => LED_TMP <= "1000111"; -- F
                when "110" => LED_TMP <= "0000001"; -- '-'
                when others => LED_TMP <= "1001111"; -- E
            end case;
        end if;
    end process;

    LED <= not LED_TMP;

end RTL;

この

when others => LED_TMP <= "1001111"; -- E

は、すべての条件を網羅*2していないときは、必須の記述です。

*1:2015.06.09修正しました。
*2:この場合におけるすべての条件とは、000-111ではありません。というのも、VHDLなどの電子回路では、0 1の他に L H Z などが存在するからです。

野球ゲームをつくる ~ 3.スイッチのトグル

ノーマルモードとチャレンジモードの切り替え用に
Tフリップフロップを使用して、スイッチのON OFF をトグルします。

togle.vhd

-- Togle T-FF
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;

entity TOGLE is
    port(
        TIN     : in  std_logic;
        RESET   : in  std_logic;
        TOUT    : out std_logic
    );
end TOGLE;

architecture RTL of TOGLE is
signal TOUT_TMP    : std_logic;

begin

    -- T-FF
    process(TIN,RESET)
    begin
        if (RESET = '0') then
            TOUT_TMP <= '0';
        elsif (TIN'event and TIN = '0') then
            TOUT_TMP <= not TOUT_TMP;
        end if;
    end process;
    
    TOUT <= TOUT_TMP;

end RTL;

野球ゲームをつくる ~ 2.分周、チャタリング除去

動作の基本となるクロックから分周回路をつくります。

元のクロックは33MHzです。
これから2Hz 8Hz 16Hz 32Hzを作成します。

divider.vhd

-- Divider
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.STD_LOGIC_UNSIGNED.ALL;

entity  DIVIDER is
    port(
        CLK         : in  std_logic;
        RESET       : in  std_logic;    
        DIV_CLK_2HZ : out std_logic;
        DIV_CLK_8Hz : out std_logic;
        DIV_CLK_16Hz: out std_logic;
        DIV_CLK_32HZ: out std_logic
    );
end DIVIDER;

architecture RTL of DIVIDER is
    constant CLK_BIT   : integer := 25;    --Osc 33MHz: 25bit
    signal DIV_COUNTER : std_logic_vector(CLK_BIT-1 downto 0);

begin

    process(CLK,RESET)
    begin
        if (RESET = '0') then
            DIV_COUNTER <= (others => '0');
        elsif (CLK'event and CLK = '0') then
            DIV_COUNTER <= DIV_COUNTER + 1;
        end if;
    end process;
    
    DIV_CLK_2HZ  <= not DIV_COUNTER(CLK_BIT-2);
    DIV_CLK_8HZ  <= not DIV_COUNTER(CLK_BIT-4);
    DIV_CLK_16HZ <= not DIV_COUNTER(CLK_BIT-5);
    DIV_CLK_32HZ <= not DIV_COUNTER(CLK_BIT-6);

end RTL;

チャタリングは64Hz = 15.625msのサイクルで除去します。
chattering_cut.vhd

-- Chattering CUT rate about 64Hz 15.625ms
-- InPutClock 33MHz
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.STD_LOGIC_UNSIGNED.ALL;

entity CHATTERING_CUT is
    port(
        SW   : in  std_logic;
        CLK  : in  std_logic;
        RESET: in  std_logic;
        COUT : out std_logic
    );
end CHATTERING_CUT;

architecture RTL of CHATTERING_CUT is
    signal DIV_COUNTER : std_logic_vector(24 downto 0);
    signal DIV_CLK_64HZ : std_logic;
begin

    process(CLK,RESET)
    begin
        if (RESET = '0') then
            DIV_COUNTER <= (others => '0');
        elsif (CLK'event and CLK = '0') then
            DIV_COUNTER <= DIV_COUNTER + 1;
        end if;
    end process;
    
    DIV_CLK_64HZ <= not DIV_COUNTER(18);
    
    process(DIV_CLK_64HZ)
    begin
        if (DIV_CLK_64HZ'event and DIV_CLK_64HZ ='0') then
            COUT <=  SW;
        end if;
    end process;
    
end RTL;

64Hzのクロックは分周からひっぱってきても良いですが、
遅延を考えると元クロックから作成したほうが良いでしょう。*1

*1:その場合、消費電力の問題が発生します。