環境はWindowsPhoneSilverLight8.1ですが、基本的にはC#+XAMLなのであまり変更せずにWinRTにも移植可能ではないかと思います。
まずは外観から

XAMLで「ChangePropertyAction」を利用して押した時の反応などを定義してあります
<UserControl
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:es="clr-namespace:Microsoft.Expression.Shapes;assembly=Microsoft.Expression.Drawing" xmlns:i="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity" xmlns:ec="clr-namespace:Microsoft.Expression.Interactivity.Core;assembly=Microsoft.Expression.Interactions" xmlns:eim="clr-namespace:Microsoft.Expression.Interactivity.Media;assembly=Microsoft.Expression.Interactions" x:Class="CryEarthWPLib.Controller.View.SingleControllerPad"
mc:Ignorable="d"
FontFamily="{StaticResource PhoneFontFamilyNormal}"
FontSize="{StaticResource PhoneFontSizeNormal}"
Foreground="{StaticResource PhoneForegroundBrush}"
d:DesignHeight="150" d:DesignWidth="150" Opacity="0.7">
<Grid x:Name="LayoutRoot" Width="150" Height="150">
<VisualStateManager.VisualStateGroups>
<VisualStateGroup x:Name="VisualStateGroup"/>
</VisualStateManager.VisualStateGroups>
<Path x:Name="TopPath" UseLayoutRounding="False" VerticalAlignment="Top" Data="M84.8528,0 L98.9949,14.1422 L97.7933,15.3155 C85.2319,27.2917 68.2234,34.6447 49.4975,34.6447 C30.1675,34.6447 12.6675,26.8097 0,14.1421 L14.1421,0 C23.1904,9.04823 35.6904,14.6447 49.4975,14.6447 C63.3046,14.6447 75.8046,9.04823 84.8528,4E-06 z" Height="34.645" Margin="25.503,35.355,25.503,0" RenderTransformOrigin="0.5,0.061905" Stretch="Fill" Stroke="#667088B2" MouseEnter="RingPath_MouseEnter" MouseLeftButtonDown="RingPath_MouseLeftButtonDown" MouseLeftButtonUp="RingPath_MouseLeftButtonUp" MouseLeave="RingPath_MouseLeave">
<i:Interaction.Triggers>
<i:EventTrigger EventName="MouseLeftButtonDown">
<ec:ChangePropertyAction TargetName="TopPath_State" PropertyName="Visibility" TargetObject="{Binding ElementName=TopPath_State}"/>
</i:EventTrigger>
<i:EventTrigger EventName="MouseEnter">
<ec:ChangePropertyAction TargetName="TopPath_State" PropertyName="Visibility" TargetObject="{Binding ElementName=TopPath_State}"/>
</i:EventTrigger>
<i:EventTrigger EventName="MouseLeftButtonUp">
<ec:ChangePropertyAction TargetName="TopPath_State" PropertyName="Visibility" TargetObject="{Binding ElementName=TopPath_State}">
<ec:ChangePropertyAction.Value>
<Visibility>Collapsed</Visibility>
</ec:ChangePropertyAction.Value>
</ec:ChangePropertyAction>
</i:EventTrigger>
<i:EventTrigger EventName="MouseLeave">
<ec:ChangePropertyAction TargetName="TopPath_State" PropertyName="Visibility" TargetObject="{Binding ElementName=TopPath_State}">
<ec:ChangePropertyAction.Value>
<Visibility>Collapsed</Visibility>
</ec:ChangePropertyAction.Value>
</ec:ChangePropertyAction>
</i:EventTrigger>
</i:Interaction.Triggers>
<Path.Fill>
<LinearGradientBrush EndPoint="0.5,1" StartPoint="0.5,0">
<GradientStop Color="#FF71A0E4" Offset="1"/>
<GradientStop Color="#FF17335B" Offset="0.246"/>
</LinearGradientBrush>
</Path.Fill>
<Path.RenderTransform>
<CompositeTransform ScaleY="-1"/>
</Path.RenderTransform>
</Path>
<Path x:Name="BottomPath" UseLayoutRounding="False" VerticalAlignment="Bottom" Data="M14.1421,0 C23.1904,9.04823 35.6904,14.6447 49.4975,14.6447 C63.3046,14.6447 75.8046,9.04823 84.8528,0 L98.995,14.1422 C86.3275,26.8097 68.8275,34.6447 49.4975,34.6447 C30.1675,34.6447 12.6675,26.8097 0,14.1422 z" Height="34.645" Margin="25.503,0,25.503,5" RenderTransformOrigin="0.5,0.061905" Stretch="Fill" Stroke="#667088B2" MouseLeftButtonDown="RingPath_MouseLeftButtonDown" MouseLeftButtonUp="RingPath_MouseLeftButtonUp" MouseLeave="RingPath_MouseLeave" MouseEnter="RingPath_MouseEnter">
<i:Interaction.Triggers>
<i:EventTrigger EventName="MouseLeftButtonDown">
<ec:ChangePropertyAction TargetName="BottomPath_State" PropertyName="Visibility"/>
</i:EventTrigger>
<i:EventTrigger EventName="MouseEnter">
<ec:ChangePropertyAction TargetName="BottomPath_State" PropertyName="Visibility"/>
</i:EventTrigger>
<i:EventTrigger EventName="MouseLeftButtonUp">
<ec:ChangePropertyAction TargetName="BottomPath_State" PropertyName="Visibility">
<ec:ChangePropertyAction.Value>
<Visibility>Collapsed</Visibility>
</ec:ChangePropertyAction.Value>
</ec:ChangePropertyAction>
</i:EventTrigger>
<i:EventTrigger EventName="MouseLeave">
<ec:ChangePropertyAction TargetName="BottomPath_State" PropertyName="Visibility">
<ec:ChangePropertyAction.Value>
<Visibility>Collapsed</Visibility>
</ec:ChangePropertyAction.Value>
</ec:ChangePropertyAction>
</i:EventTrigger>
</i:Interaction.Triggers>
<Path.Fill>
<LinearGradientBrush EndPoint="0.5,1" StartPoint="0.5,0">
<GradientStop Color="#FF71A0E4" Offset="1"/>
<GradientStop Color="#FF17335B" Offset="0.246"/>
</LinearGradientBrush>
</Path.Fill>
<Path.RenderTransform>
<CompositeTransform/>
</Path.RenderTransform>
</Path>
<Path x:Name="RightPath" Data="M14.1422,0 C26.8097,12.6675 34.6447,30.1675 34.6447,49.4975 C34.6447,68.2234 27.2917,85.2319 15.3155,97.7933 L14.1422,98.995 L0,84.8528 C9.04823,75.8046 14.6447,63.3046 14.6447,49.4975 C14.6447,36.5533 9.72592,24.758 1.65557,15.8786 L0,14.1422 z" Margin="110.355,25.503,5,25.503" Stretch="Fill" UseLayoutRounding="False" Stroke="#667088B2" MouseLeftButtonDown="RingPath_MouseLeftButtonDown" MouseLeftButtonUp="RingPath_MouseLeftButtonUp" MouseLeave="RingPath_MouseLeave" MouseEnter="RingPath_MouseEnter">
<i:Interaction.Triggers>
<i:EventTrigger EventName="MouseLeftButtonDown">
<ec:ChangePropertyAction TargetName="RightPath_State" PropertyName="Visibility" TargetObject="{Binding ElementName=RightPath_State}"/>
</i:EventTrigger>
<i:EventTrigger EventName="MouseEnter">
<ec:ChangePropertyAction TargetName="RightPath_State" PropertyName="Visibility" TargetObject="{Binding ElementName=RightPath_State}"/>
</i:EventTrigger>
<i:EventTrigger EventName="MouseLeftButtonUp">
<ec:ChangePropertyAction TargetName="RightPath_State" PropertyName="Visibility" TargetObject="{Binding ElementName=RightPath_State}">
<ec:ChangePropertyAction.Value>
<Visibility>Collapsed</Visibility>
</ec:ChangePropertyAction.Value>
</ec:ChangePropertyAction>
</i:EventTrigger>
<i:EventTrigger EventName="MouseLeave">
<ec:ChangePropertyAction TargetName="RightPath_State" PropertyName="Visibility" TargetObject="{Binding ElementName=RightPath_State}">
<ec:ChangePropertyAction.Value>
<Visibility>Collapsed</Visibility>
</ec:ChangePropertyAction.Value>
</ec:ChangePropertyAction>
</i:EventTrigger>
</i:Interaction.Triggers>
<Path.Fill>
<LinearGradientBrush EndPoint="0.5,1" MappingMode="RelativeToBoundingBox" StartPoint="0.5,0">
<LinearGradientBrush.RelativeTransform>
<CompositeTransform CenterY="0.5" CenterX="0.5" Rotation="-90"/>
</LinearGradientBrush.RelativeTransform>
<GradientStop Color="#FF71A0E4" Offset="1"/>
<GradientStop Color="#FF17335B" Offset="0.246"/>
</LinearGradientBrush>
</Path.Fill>
</Path>
<Path x:Name="LeftPath" UseLayoutRounding="False" HorizontalAlignment="Left" VerticalAlignment="Center" Data="M84.8528,0 L98.9949,14.1422 L97.7933,15.3155 C85.2319,27.2917 68.2234,34.6447 49.4975,34.6447 C30.1675,34.6447 12.6675,26.8097 0,14.1421 L14.1421,0 C23.1904,9.04823 35.6904,14.6447 49.4975,14.6447 C63.3046,14.6447 75.8046,9.04823 84.8528,4E-06 z" Height="34.645" Margin="-11.997,72.855,0,42.5" RenderTransformOrigin="0.5,0.061905" Stretch="Fill" Width="98.994" Stroke="#667088B2" MouseLeftButtonDown="RingPath_MouseLeftButtonDown" MouseLeftButtonUp="RingPath_MouseLeftButtonUp" MouseLeave="RingPath_MouseLeave" MouseEnter="RingPath_MouseEnter">
<i:Interaction.Triggers>
<i:EventTrigger EventName="MouseLeftButtonDown">
<ec:ChangePropertyAction TargetName="LeftPath_State" PropertyName="Visibility"/>
</i:EventTrigger>
<i:EventTrigger EventName="MouseEnter">
<ec:ChangePropertyAction TargetName="LeftPath_State" PropertyName="Visibility"/>
</i:EventTrigger>
<i:EventTrigger EventName="MouseLeftButtonUp">
<ec:ChangePropertyAction TargetName="LeftPath_State" PropertyName="Visibility">
<ec:ChangePropertyAction.Value>
<Visibility>Collapsed</Visibility>
</ec:ChangePropertyAction.Value>
</ec:ChangePropertyAction>
</i:EventTrigger>
<i:EventTrigger EventName="MouseLeave">
<ec:ChangePropertyAction TargetName="LeftPath_State" PropertyName="Visibility">
<ec:ChangePropertyAction.Value>
<Visibility>Collapsed</Visibility>
</ec:ChangePropertyAction.Value>
</ec:ChangePropertyAction>
</i:EventTrigger>
</i:Interaction.Triggers>
<Path.Fill>
<LinearGradientBrush EndPoint="0.5,1" StartPoint="0.5,0">
<GradientStop Color="#FF71A0E4" Offset="1"/>
<GradientStop Color="#FF17335B" Offset="0.246"/>
</LinearGradientBrush>
</Path.Fill>
<Path.RenderTransform>
<CompositeTransform Rotation="90"/>
</Path.RenderTransform>
</Path>
<es:Arc x:Name="TapArc" ArcThickness="1" ArcThicknessUnit="Percent" EndAngle="360" Height="40" Stretch="None" UseLayoutRounding="False" Width="40" HorizontalAlignment="Center" VerticalAlignment="Center" Tap="TapArc_Tap" Stroke="#33999DCB" d:IsLocked="True">
<i:Interaction.Triggers>
<i:EventTrigger EventName="MouseLeftButtonDown">
<ec:ChangePropertyAction TargetName="TapArc_State" PropertyName="Visibility"/>
</i:EventTrigger>
<i:EventTrigger EventName="MouseLeftButtonUp">
<ec:ChangePropertyAction TargetName="TapArc_State" PropertyName="Visibility">
<ec:ChangePropertyAction.Value>
<Visibility>Collapsed</Visibility>
</ec:ChangePropertyAction.Value>
</ec:ChangePropertyAction>
</i:EventTrigger>
<i:EventTrigger EventName="MouseLeave">
<ec:ChangePropertyAction TargetName="TapArc_State" PropertyName="Visibility">
<ec:ChangePropertyAction.Value>
<Visibility>Collapsed</Visibility>
</ec:ChangePropertyAction.Value>
</ec:ChangePropertyAction>
</i:EventTrigger>
</i:Interaction.Triggers>
<es:Arc.Fill>
<RadialGradientBrush>
<GradientStop Color="#FF2E4A93" Offset="0.884"/>
<GradientStop Color="Black"/>
<GradientStop Color="#FF4D77E8" Offset="0.53"/>
<GradientStop Offset="1" Color="Transparent"/>
<GradientStop Color="#FF162D66" Offset="0.944"/>
<GradientStop Color="#FF2E4A93" Offset="0.19"/>
</RadialGradientBrush>
</es:Arc.Fill>
</es:Arc>
<es:RegularPolygon x:Name="TopTriangle" HorizontalAlignment="Center" Height="15" InnerRadius="1" PointCount="3" Stretch="Fill" UseLayoutRounding="False" VerticalAlignment="Top" Width="15" Fill="#66FFFFFF" Margin="67.5,7,67.5,0" IsHitTestVisible="False" d:IsLocked="True"/>
<es:RegularPolygon x:Name="BottomTriangle" HorizontalAlignment="Center" Height="15" InnerRadius="1" PointCount="3" Stretch="Fill" UseLayoutRounding="False" VerticalAlignment="Bottom" Width="15" Fill="#66FFFFFF" Margin="67.5,0,67.5,7" RenderTransformOrigin="0.5,0.5" IsHitTestVisible="False" d:IsLocked="True">
<es:RegularPolygon.RenderTransform>
<CompositeTransform ScaleY="-1"/>
</es:RegularPolygon.RenderTransform>
</es:RegularPolygon>
<es:RegularPolygon x:Name="LeftTriangle" HorizontalAlignment="Left" Height="15" InnerRadius="1" PointCount="3" Stretch="Fill" UseLayoutRounding="False" VerticalAlignment="Center" Width="15" Fill="#66FFFFFF" RenderTransformOrigin="0.5,0.5" Margin="7,0,0,0" IsHitTestVisible="False" d:IsLocked="True">
<es:RegularPolygon.RenderTransform>
<CompositeTransform ScaleY="-1" Rotation="90"/>
</es:RegularPolygon.RenderTransform>
</es:RegularPolygon>
<es:RegularPolygon x:Name="RightTriangle" HorizontalAlignment="Right" Height="15" InnerRadius="1" PointCount="3" Stretch="Fill" UseLayoutRounding="False" VerticalAlignment="Center" Width="15" Fill="#66FFFFFF" RenderTransformOrigin="0.5,0.5" Margin="0,0,7,0" IsHitTestVisible="False" d:IsLocked="True">
<es:RegularPolygon.RenderTransform>
<CompositeTransform Rotation="-90" ScaleY="-1" ScaleX="-1"/>
</es:RegularPolygon.RenderTransform>
</es:RegularPolygon>
<Path x:Name="TopPath_State" UseLayoutRounding="False" VerticalAlignment="Top" Data="M84.8528,0 L98.9949,14.1422 L97.7933,15.3155 C85.2319,27.2917 68.2234,34.6447 49.4975,34.6447 C30.1675,34.6447 12.6675,26.8097 0,14.1421 L14.1421,0 C23.1904,9.04823 35.6904,14.6447 49.4975,14.6447 C63.3046,14.6447 75.8046,9.04823 84.8528,4E-06 z" Height="34.645" Margin="25.503,35.355,25.503,0" RenderTransformOrigin="0.5,0.061905" Stretch="Fill" Stroke="#667088B2" Fill="#99FFFFFF" Visibility="Collapsed" IsHitTestVisible="False" d:IsLocked="True">
<Path.RenderTransform>
<CompositeTransform ScaleY="-1"/>
</Path.RenderTransform>
</Path>
<Path x:Name="RightPath_State" Data="M14.1422,0 C26.8097,12.6675 34.6447,30.1675 34.6447,49.4975 C34.6447,68.2234 27.2917,85.2319 15.3155,97.7933 L14.1422,98.995 L0,84.8528 C9.04823,75.8046 14.6447,63.3046 14.6447,49.4975 C14.6447,36.5533 9.72592,24.758 1.65557,15.8786 L0,14.1422 z" Margin="110.355,25.503,5,25.503" Stretch="Fill" UseLayoutRounding="False" Stroke="#667088B2" Fill="#99FFFFFF" Visibility="Collapsed" IsHitTestVisible="False" d:IsLocked="True"/>
<Path x:Name="BottomPath_State" UseLayoutRounding="False" VerticalAlignment="Bottom" Data="M14.1421,0 C23.1904,9.04823 35.6904,14.6447 49.4975,14.6447 C63.3046,14.6447 75.8046,9.04823 84.8528,0 L98.995,14.1422 C86.3275,26.8097 68.8275,34.6447 49.4975,34.6447 C30.1675,34.6447 12.6675,26.8097 0,14.1422 z" Height="34.645" Margin="25.503,0,25.503,5" RenderTransformOrigin="0.5,0.061905" Stretch="Fill" Stroke="#667088B2" Fill="#99FFFFFF" Visibility="Collapsed" IsHitTestVisible="False" d:IsLocked="True">
<Path.RenderTransform>
<CompositeTransform/>
</Path.RenderTransform>
</Path>
<Path x:Name="LeftPath_State" UseLayoutRounding="False" HorizontalAlignment="Left" VerticalAlignment="Center" Data="M84.8528,0 L98.9949,14.1422 L97.7933,15.3155 C85.2319,27.2917 68.2234,34.6447 49.4975,34.6447 C30.1675,34.6447 12.6675,26.8097 0,14.1421 L14.1421,0 C23.1904,9.04823 35.6904,14.6447 49.4975,14.6447 C63.3046,14.6447 75.8046,9.04823 84.8528,4E-06 z" Height="34.645" Margin="-11.997,72.855,0,42.5" RenderTransformOrigin="0.5,0.061905" Stretch="Fill" Width="98.994" Stroke="#667088B2" Fill="#99FFFFFF" Visibility="Collapsed" IsHitTestVisible="False" d:IsLocked="True">
<Path.RenderTransform>
<CompositeTransform Rotation="90"/>
</Path.RenderTransform>
</Path>
<es:Arc x:Name="TapArc_State" ArcThickness="1" ArcThicknessUnit="Percent" EndAngle="360" Height="40" Stretch="None" UseLayoutRounding="False" Width="40" HorizontalAlignment="Center" VerticalAlignment="Center" Tap="TapArc_Tap" Stroke="#33999DCB" Margin="55" Fill="#99FFFFFF" Visibility="Collapsed" IsHitTestVisible="False" d:IsLocked="True"/>
</Grid>
</UserControl>
続いてコード部分
public partial class SingleControllerPad : UserControl
{
/// <summary>
/// Key判断イベントHANDLE
/// </summary>
/// <param name="sender">this</param>
/// <param name="keyId">0=Enter,1=TOP,2=Bottom,3=Left,4=Right,5=non</param>
public delegate void TapKeyControlEventHandler(object sender, int keyId);
public event TapKeyControlEventHandler TapKeyControl;
/// <summary>
/// Keyイベント
/// </summary>
/// <param name="keyId">0=Enter,1=TOP,2=Bottom,3=Left,4=Right,5=non</param>
protected virtual void OnTopKeyControl(int keyId)
{
if (TapKeyControl != null)
{
TapKeyControl(this, keyId);
}
}
public SingleControllerPad()
{
InitializeComponent();
}
/// <summary>
/// 確定Button
/// 所謂Aボタンを押したとか
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void TapArc_Tap(object sender, System.Windows.Input.GestureEventArgs e)
{
// 確定なので0で固定
// イベントをチェックしているところでイベント発生後、イベントをリセットする
OnTopKeyControl(0);
}
/// <summary>
/// 領域に入ってきたことを検知
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void RingPath_MouseEnter(object sender, System.Windows.Input.MouseEventArgs e)
{
var ul = e.OriginalSource as UIElement;
if (ul != null)
{
var name = ((System.Windows.Shapes.Path)ul).Name;
CallRingPathEvent(name);
}
}
/// <summary>
/// 領域から出たことを検知
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void RingPath_MouseLeave(object sender, System.Windows.Input.MouseEventArgs e)
{
// 何も押していないことを通知
CallRingPathEvent("");
}
/// <summary>
/// Buttonを選択
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void RingPath_MouseLeftButtonDown(object sender, System.Windows.Input.MouseButtonEventArgs e)
{
var ul = e.OriginalSource as UIElement;
if (ul != null)
{
var name = ((System.Windows.Shapes.Path)ul).Name;
CallRingPathEvent(name);
}
}
/// <summary>
/// Buttonを離す
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void RingPath_MouseLeftButtonUp(object sender, System.Windows.Input.MouseButtonEventArgs e)
{
var ul = e.OriginalSource as UIElement;
if (ul != null)
{
var name = ((System.Windows.Shapes.Path)ul).Name;
CallRingPathEvent(name);
}
}
/// <summary>
/// 外部にどのボタンを押しているかを知らせる
/// </summary>
/// <param name="pathName"></param>
void CallRingPathEvent(string pathName)
{
switch (pathName)
{
case "TopPath":
OnTopKeyControl(1);
break;
case "BottomPath":
OnTopKeyControl(2);
break;
case "LeftPath":
OnTopKeyControl(3);
break;
case "RightPath":
OnTopKeyControl(4);
break;
default:
OnTopKeyControl(5);
break;
}
}
}
このコントロール自体はあくまでもキー情報を通知するだけの機能にしています。
使うときは、イベントでKeyIDを確保して、ゲームループでそのIDを判断して各操作を行うという形にしています。
{
var timer = ThreadPoolTimer.CreatePeriodicTimer(new TimerElapsedHandler(GameLoopTimerCallback), TimeSpan.FromMilliseconds(35));
// メインUIのDispatcherを取得
dispatcher = Windows.ApplicationModel.Core.CoreApplication.MainView.CoreWindow.Dispatcher;
}
private async void GameLoopTimerCallback(ThreadPoolTimer timer)
{
if (dispatcher != null)
await dispatcher.RunAsync(CoreDispatcherPriority.Normal,
() =>
{
// 各種処理関数を呼び出す
});
}
大分手抜き&片手コントロール用なのでいろいろ不足していますし、キャンセルボタンの扱いという問題もあります。
まあ、入り口ということで、こんなのもありかなと思います。