2013年1月16日水曜日

WPF + DxLib (C#) - その2

前回のコードをゲーム開発に使いやすいよう改造

1. FPS調整クラス作成

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Diagnostics;
namespace WpfApplication1
{
/// <summary>
/// フレーム調整
/// </summary>
public class FpsTicker : Stopwatch
{
public double Fps { get; private set; }
public int SkipTimes { get; private set; }
public double ElpasedSec { get { return ElapsedTicks / (double)Stopwatch.Frequency; } }
private double prevSec = 0.0;
/// <summary>
/// 開始
/// </summary>
public new void Start()
{
SkipTimes = 0;
prevSec = ElpasedSec;
base.Start();
}
/// <summary>
/// フレーム待ち処理
/// </summary>
/// <param name="fps"></param>
/// <returns>true:描画実行 false:描画スキップ</returns>
public bool Wait(int fps = 60)
{
double waitSec = 1.0 / fps;
double nowSec = ElpasedSec;
double diffSec = nowSec - prevSec;
if (diffSec > waitSec)
{
SkipTimes++;
prevSec = nowSec;
return false;
}
SkipTimes = 0;
while (diffSec < waitSec)
{
nowSec = ElpasedSec;
diffSec = nowSec - prevSec;
}
prevSec = nowSec - (diffSec - waitSec);
Fps = 1.0 / diffSec;
return true;
}
public override string ToString()
{
return string.Format("{0:#0.00} ({1})", Fps, SkipTimes);
}
}
}
view raw gistfile1.cs hosted with ❤ by GitHub
2. DxLibImageクラスにゲームループ追加

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Interop;
using System.Windows.Media;
using System.Windows.Threading;
using System.Diagnostics;
using DxLibDLL;
namespace WpfApplication1
{
public class DxLibImage : D3DImage
{
private DX.SetRestoreGraphCallbackCallback RestoreGraphCallback;
private FpsTicker fpsTicker = new FpsTicker();
public event EventHandler Reset;
public event EventHandler Update;
public event EventHandler Render;
public int BufferWidth { get; set; }
public int BufferHeight { get; set; }
public string Fps { get { return fpsTicker.ToString(); } }
/// <summary>
/// コンストラクタ
/// </summary>
public DxLibImage()
{
BufferWidth = 800;
BufferHeight = 600;
}
/// <summary>
/// DxLibの初期化
/// </summary>
public void Init_DxLib()
{
if (DX.DxLib_IsInit() == DX.TRUE)
DX.DxLib_End();
HwndSource hwnd = new HwndSource(0, 0, 0, 0, 0, "DxLib", IntPtr.Zero);
DX.SetUserWindow(hwnd.Handle); // 描画ウィンドウの設定
DX.SetGraphMode(BufferWidth, BufferHeight, 32); // グラフィックモードの設定
DX.SetAlwaysRunFlag(DX.TRUE); // 非アクティブ時も処理続行
DX.SetDrawScreen(DX.DX_SCREEN_BACK); // 描画先をバックバッファへ設定
DX.SetUseFPUPreserveFlag(DX.TRUE); // FPUの精度を落とさない
DX.SetWaitVSyncFlag(DX.FALSE); // VSync同期を無効
DX.SetOutApplicationLogValidFlag(DX.FALSE); // ログ出力停止
DX.SetDoubleStartValidFlag(DX.TRUE); // 多重起動を許可
DX.SetUseIMEFlag(DX.TRUE); // IMEを有効
DX.SetBackgroundColor(0, 0, 0);
if (DX.DxLib_Init() == -1)
{
throw new Exception("Dxlib_Init");
}
// GC対策(不要?)
DX.SetUseGraphBaseDataBackup(DX.FALSE); // 自分ででデバイスロスト時の処理を行う
RestoreGraphCallback = RestoreGraph;
DX.SetRestoreGraphCallback(RestoreGraphCallback); // デバイスリセット時のイベントを設定
// メインループ
ComponentDispatcher.ThreadIdle += ComponentDispatcher_ThreadIdle;
// 描画ループ
CompositionTarget.Rendering += CompositionTarget_Rendering;
IsFrontBufferAvailableChanged += d3dImage_IsFrontBufferAvailableChanged;
SetBackBuffer();
fpsTicker.Start();
}
/// <summary>
/// メインループ
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
void ComponentDispatcher_ThreadIdle(object sender, EventArgs e)
{
OnUpdate();
if (fpsTicker.Wait())
{
DX.ClearDrawScreen();
OnRender();
DX.ScreenFlip();
}
}
/// <summary>
/// デバイスリセット発生
/// </summary>
void RestoreGraph()
{
OnReset();
}
/// <summary>
/// バックバッファの設定
/// </summary>
void SetBackBuffer()
{
Lock();
SetBackBuffer(D3DResourceType.IDirect3DSurface9, DX.GetUseDirect3D9BackBufferSurface());
Unlock();
}
/// <summary>
/// 表示の更新
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
void CompositionTarget_Rendering(object sender, EventArgs e)
{
try
{
if (IsFrontBufferAvailable)
{
Lock();
SetBackBuffer(D3DResourceType.IDirect3DSurface9, DX.GetUseDirect3D9BackBufferSurface());
AddDirtyRect(new Int32Rect(0, 0, PixelWidth, PixelHeight));
Unlock();
}
}
catch (Exception ex)
{
Trace.WriteLine(ex.ToString());
}
}
/// <summary>
/// フロントバッファの更新
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
void d3dImage_IsFrontBufferAvailableChanged(object sender, DependencyPropertyChangedEventArgs e)
{
fpsTicker.Stop();
if (IsFrontBufferAvailable)
{
CompositionTarget.Rendering += CompositionTarget_Rendering;
}
else
{
CompositionTarget.Rendering -= CompositionTarget_Rendering;
}
fpsTicker.Start();
}
/// <summary>
/// デバイスロスト発生
/// </summary>
public virtual void OnReset()
{
if (Reset != null)
Reset(null, EventArgs.Empty);
}
/// <summary>
/// プログラム更新処理
/// </summary>
public virtual void OnUpdate()
{
if (Update != null)
Update(null, EventArgs.Empty);
}
/// <summary>
/// 描画処理
/// </summary>
public virtual void OnRender()
{
if (Render != null)
Render(null, EventArgs.Empty);
}
/// <summary>
/// DxLibの開放
/// </summary>
public void End_DxLib()
{
if (DX.DxLib_IsInit() != DX.TRUE)
return;
DX.DxLib_End();
}
}
}
view raw gistfile1.cs hosted with ❤ by GitHub
3. Windowのデザインをゲーム用に変更
<Window x:Class="WpfApplication1.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:me="clr-namespace:WpfApplication1"
Title="MainWindow" Loaded="Window_Loaded" Closed="Window_Closed" SizeToContent="WidthAndHeight" ResizeMode="CanMinimize">
<Canvas Width="800" Height="600">
<Image>
<Image.Source>
<me:DxLibImage x:Name="dxImage" Render="dxImage_Render" Reset="dxImage_Reset" Update="dxImage_Update" />
</Image.Source>
</Image>
<TextBlock x:Name="debugText" Height="600" Canvas.Left="600" TextWrapping="Wrap" Canvas.Top="0" Width="200" Foreground="Yellow" TextAlignment="Right"/>
</Canvas>
</Window>
view raw gistfile1.xml hosted with ❤ by GitHub
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using System.Windows.Interop;
using System.Diagnostics;
using DxLibDLL;
namespace WpfApplication1
{
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
private void Window_Loaded(object sender, RoutedEventArgs e)
{
// DxLibの初期化
dxImage.Init_DxLib();
}
private void Window_Closed(object sender, EventArgs e)
{
// DxLibの終了
dxImage.End_DxLib();
}
private void dxImage_Reset(object sender, EventArgs e)
{
// グラフィックのリセット
}
private void dxImage_Update(object sender, EventArgs e)
{
// プログラムの更新処理
debugText.Text = dxImage.Fps;
}
private void dxImage_Render(object sender, EventArgs e)
{
// 描画処理
if (this.WindowState == WindowState.Minimized)
return;
DX.DrawBox(0, 0, 200, 200, DX.GetColor(255, 0, 0), DX.TRUE);
}
}
}
view raw gistfile1.cs hosted with ❤ by GitHub

1 件のコメント:

  1. 超今更ですが
    自分の環境ではInit前に
    DX.SetUseDirect3DVersion(DX.DX_DIRECT3D_9EX);
    を指定しないとBackBafferが取得できませんでした

    返信削除