2011年11月12日

Twitter用クライアント『TLBar』 Ver.0.0.0.17公開

Twitter用クライアント TLBar (表示専用、.NET Framework 4使用、oAuth認証)Ver.0.0.0.17公開

Twitter用のクライアント「TLBar」Ver.0.0.0.17を公開しました。
TLBar -Amonution

ブログの更新が途絶えていたので、こちらでも告知します。
今回の更新では「リピート機能」を追加しました。こちらはユーザーさんからの要望を受けて実装したものです。
リピート機能を有効にしていると、タイムラインの未読が全てなくなってから、最新のツイートから指定した件数分だけくり返し表示する状態になります。
新しくツイートがあればそれを表示しますが、また未読がなくなればくり返し表示する状態になります。

あとは内部処理はいろいろといじっています。もしかしたら、そのせいで何か問題が起きるかもしれませんが…。
何かあったらツイッターで教えていただけると助かります。

良かったらクリックしてください
にほんブログ村 IT技術ブログ プログラム・プログラマーへ  人気ブログランキングへ

posted by among at 22:17 | Comment(0) | TrackBack(0) | Twitter

2011年09月19日

ListViewのオーナードローの問題(1)(C#メモ)

ListViewの詳細ビューで奇数行と偶数行を変えるような使い方を良く目にしますが、 これを実現するためにはオーナードローを使って自前で描画をしてやる必要があります。
これ自体はそれほど難しい事ではないです。下記のようにやれば実現出来ます。

// ListViewコントロール
public ListView listView1;

private void Form1_Load(object sender, EventArgs e)
{
    // ListViewを生成して配置する
    listView1 = new ListView();
    this.Controls.Add(listView1);

    // オーナードローの設定
    listView1.OwnerDraw = true;
    // Headerの表示をコントロールする為にイベントを追加
    listView1.DrawColumnHeader
        += new DrawListViewColumnHeaderEventHandler(
                    listView1_DrawColumnHeader);
    // Itemの表示をコントロールする為にイベントを追加
    listView1.DrawSubItem 
        += new DrawListViewSubItemEventHandler(
                    listView1_DrawSubItem);

    // プロパティの設定
    listView1.Dock = DockStyle.Fill; // 親(Form)全体に広げる
    listView1.View = View.Details;   // 詳細ビューを指定
    listView1.MultiSelect = false;   // 複数行の選択を禁止
    listView1.FullRowSelect = true;  // 項目を選択すると行全体を選択
    listView1.GridLines = true;      // グリッド線を表示
    
    // 列の作成
    listView1.Columns.Add("id", "ID", 120);
    listView1.Columns.Add("data", "データ", 120);

    // アイテムの追加
    for (int i = 1; i <= 10; i++)
    {
        string[] items = { i.ToString("d2"), "データ" + i.ToString("d2") };
        listView1.Items.Add(new ListViewItem(items));
    }
}

private void listView1_DrawSubItem(
                    object sender, 
                    DrawListViewSubItemEventArgs e)
{
    // 選択されているアイテムの描画
    if ((e.ItemState & ListViewItemStates.Selected) 
                    == ListViewItemStates.Selected)
    {
        e.Graphics.FillRectangle(
                        SystemBrushes.Highlight, 
                        e.Bounds);
        e.Graphics.DrawString(
                        e.SubItem.Text, 
                        e.Item.Font, 
                        SystemBrushes.HighlightText, 
                        e.Bounds);
        return;
    }

    // 奇数行は背景色を変更して描画
    if ((e.Item.Index % 2) != 0)
    {
        e.Graphics.FillRectangle(
                        Brushes.LightYellow, 
                        e.Bounds);
        e.Graphics.DrawString(
                        e.SubItem.Text, 
                        e.Item.Font, 
                        SystemBrushes.WindowText, 
                        e.Bounds);
        return;
    }

    // 偶数行はデフォルト描画
    {
        e.Graphics.FillRectangle(
                        SystemBrushes.Window, 
                        e.Bounds);
        e.Graphics.DrawString(
                        e.SubItem.Text, 
                        e.Item.Font, 
                        SystemBrushes.WindowText, 
                        e.Bounds);
        return;
    }
}

private void listView1_DrawColumnHeader(
                        object sender, 
                        DrawListViewColumnHeaderEventArgs e)
{
    // 描画はOSに任せる
    e.DrawDefault = true;
}

実行するとこのような感じになります。
listview-ownerdraw-1.png

ここまでは問題ないのですが、ここでツールヒントを使うように設定を変更してみます。

// Form1_Loadに下記の行を追加
listView1.ShowItemToolTips = true;   // ツールヒント表示

すると、選択された行の描画が上手くいかなくなります。
具体的には、選択された行にマウスカーソルを置くと、選択が解除されたような感じになります。

listview-ownerdraw-2.png

原因を調べてみると、どうやらDrawSubItemに渡される引数DrawListViewSubItemEventArgsのItemStateにSelectedのビットがONになっていないケースがあるようです。

DrawSubItemでItemStateをリストに描画するように改造します。

private void listView1_DrawSubItem(
                        object sender, 
                        DrawListViewSubItemEventArgs e)
{
    // 選択されているアイテムの描画
    if ((e.ItemState & ListViewItemStates.Selected) 
                    == ListViewItemStates.Selected)
    {
        e.Graphics.FillRectangle(
                        SystemBrushes.Highlight, 
                        e.Bounds);
        e.Graphics.DrawString(
                        e.ItemState.ToString(), 
                        e.Item.Font, 
                        SystemBrushes.HighlightText, 
                        e.Bounds);
        return;
    }

    // 奇数行は背景色を変更して描画
    if ((e.Item.Index % 2) != 0)
    {
        e.Graphics.FillRectangle(
                        Brushes.LightYellow, 
                        e.Bounds);
        e.Graphics.DrawString(
                        e.ItemState.ToString(), 
                        e.Item.Font, 
                        SystemBrushes.WindowText, 
                        e.Bounds);
        return;
    }

    // 偶数行はデフォルト描画
    {
        e.Graphics.FillRectangle(
                        SystemBrushes.Window, 
                        e.Bounds);
        e.Graphics.DrawString(
                        e.ItemState.ToString(), 
                        e.Item.Font, 
                        SystemBrushes.WindowText, 
                        e.Bounds);
        return;
    }
}

実行してみると、選択行は「Selected, Focused, Hot, ShowKeyboardCues」となりますが、 カーソルを上に置いてしばらくすると「0」になってしまいます。

listview-ownerdraw-3.png

なぜ、このようなことになるのか、調べてみたのですが良く分かりませんでした。 おそらくは、ツールヒントを表示する処理の中で呼び出されているのだと思いますが…。

結局、回避する方法は分からず、ツールヒントを使用しないぐらいしか対応策は考えつきませんでした。

ListViewの詳細ビューのオーナードローには、他にも問題(というか知らないとトラップにはまること)があるので、別の機会に書きたいと思います。
(実はMSDNにちゃんと書いてあるかもしれませんが…気づいてないだけで。)

良かったらクリックしてください
にほんブログ村 IT技術ブログ プログラム・プログラマーへ  人気ブログランキングへ

posted by among at 21:44 | Comment(0) | TrackBack(0) | C#

2011年08月21日

乱数のコントロール

乱数のコントロール

ゲームにおいて乱数というのは多くの場面で使われています。テトリスで次にどんな形状が出現するか、RPGで敵にエンカウントするかどうか、シューティングで敵がどの方向に弾を撃つか。そのような場面で乱数が使われます。

しかし、乱数というのは偏りがあります。長いスパンでみれば、出現する乱数の数値は均一になると思いますが、一時的には偏ることがあります。例えば、テトリスでいつまでたってもテトリス棒が落ちてこない。あるいは、四角ばっかり落ちてくる、そんな経験はないでしょうか。これは乱数の偏りによるものです。

(これはこれで「良し」としても構いません。それに対する攻略も生まれます。)

乱数のコントロールは重要ですが、軽視されがちです。普通のプレイヤーであれば、気づいていないことも多いと思います。(逆にゲームをやりこんでいる人にとっては、乱数のことをすごく研究している人もいると思います。「電源パターン」と呼ばれる攻略法も存在します。)

具体例

テトリスで次にどんな形状が登場するか

昔のテトリス(ゲームセンターに置いてあるセガのテトリス:恐竜とかサルがでてくるやつ,初代ゲームボーイのテトリス)では、乱数により7種類のブロックが落ちてきます。そのため乱数の偏りを受けて、同一ブロックが連続して出現したり、逆に期待するブロックがいつまでも落ちてこないことがあります。

テトリスの場合は、7種類のブロックが均一に出現するのが良いはずです。

近年のテトリス(アリカのTGMシリーズ,DSのテトリスなど)では、独自のアルゴリズムにより偏りを無くすようにしています。アリカのTGMシリーズでは、過去4個の出現したブロックを履歴として記憶しておき、連続するブロックは出現しにくいようになっています。DSのテトリスでは、ブロックの種類を乱数で決めるのではなく、7種のブロックの順番を乱数で決めることで、出現率を一定にしています。

RPGで敵にエンカウントするかどうか

一般的にエンカウント率を設定することで調整します。が、調整したいのは、ある街からある街までの間に何回エンカウントするか、ということを決めたいわけです。10歩あたり1回のエンカウント、と決めたとしても、乱数の偏りによっては1回もエンカウントしないこともありますし、一歩歩く毎にエンカウントすることもあります。

かといって、10歩でエンカウント、と決めてしまうと揺らぎがなくなり、ゲームとして面白くなくなります。

そこで、エンカウント率を低く設定しておき、一歩歩く毎にエンカウント率を上昇させる。最大で10歩歩いたら確実にエンカウントする。などのようにすることで、乱数によるばらつきがありつつも、エンカウント回数を制御することができます。

他には、街の近くではエンカウントしづらい、洞窟などのダンジョンの中ではエンカウントしやすい、扉を開けた直後や画面切り替えの直後にエンカウントしやすい、敵から逃亡した直後はエンカウントしやすい、などといった調整を行ったりします。

シューティングで敵がどの方向に弾を撃つか

シューティングの敵キャラは、時にランダムで弾をばらまきますが、方向を乱数によって決めます。これが偏りがあると、簡単に「安全地帯」が生まれたり、逆に「脱出不可能」になってしまいます。

これを防ぐために、ある程度の範囲を絞って乱数を使います。例えば弾幕系シューティングなどで360度に弾をばらまくとき、乱数で360度を範囲に決めてしまうと、ある角度に偏った弾幕や隙間が生まれることがあります。そこで、10度ずつ36回に分けて乱数を使うと、乱数によるばらつきはありつつも、均等に360度にばらまくことができます。

最後に

冒頭にも書きましたが、乱数のコントロールは軽視されがちですが、重要です。乱数によるばらつきが大きくなれば、ゲームの難易度にも影響が出てきます。

重要なのは、制作側が意図した範囲内に乱数の結果を収めることと、コントロールしすぎてプレイヤー側が冷めない程度にすること、だと思います。(その調整が難しいのですが…)

良かったらクリックしてください
にほんブログ村 IT技術ブログ プログラム・プログラマーへ  人気ブログランキングへ

タグ:ゲーム制作
posted by among at 15:41 | Comment(0) | TrackBack(0) | ゲーム制作