大家好日子,
我有一个仅限触摸的应用程序,我想创建一个弹弓交互,例如,你可以在池游戏中找到它。像“愤怒的小鸟”之类的东西,但没有引力的物理特性,只有惯性和减速。
要大致了解一下:
目前,我在Canvas中有一个BouncingUserControl。用户应该能够做两件事:
如果他使用一个触摸点:拖放BouncingUserControl,然后以一定的速度抛出它。 BouncingUserControl在释放后,将从主窗口的边界反弹并停留在所述窗口内。
如果他在拖动BouncingUserControl 时使用第二个触摸点,则会出现一种弹性:两个触点越远,弹性就越紧张。释放两个触摸点中的一个后,BouncingUserControl将以相反方向的初始速度(取决于“弹性”有多紧张)抛出。
从编程上讲,我走到这一步:
这是我的BouncingUserControl,一个简单的椭圆:
<UserControl x:Class="Slingshot.BouncingUserControl"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="300"
ManipulationCompleted="ParentCanvasOnManipulationCompleted"
ManipulationInertiaStarting="ParentCanvasOnManipulationInertiaStarting"
ManipulationDelta="ParentCanvasOnManipulationDelta"
ManipulationStarting="ParentCanvasOnManipulationStarting"
>
<Grid>
<Ellipse Width="50" Height="50" Fill="Beige"/>
</Grid>
</UserControl>
背后的代码:
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;
namespace Slingshot
{
/// <summary>
/// Logique d'interaction pour BouncingUserControl.xaml
/// </summary>
public partial class BouncingUserControl : UserControl
{
public enum HorizontalEdgeBounceType
{
None,
Left,
Right
}
public enum VerticalEdgeBounceType
{
None,
Top,
Bottom
}
public BouncingUserControl()
{
InitializeComponent();
}
private HorizontalEdgeBounceType CurrentHorizontalBounce { get; set; }
private VerticalEdgeBounceType CurrentVerticalBounce { get; set; }
public void ParentCanvasOnManipulationInertiaStarting(object sender, ManipulationInertiaStartingEventArgs args)
{
((MainWindow)Window.GetWindow(this)).IsCurrentlyManipulating = false;
((MainWindow)Window.GetWindow(this)).currentlyManipulatedControl = null;
var element = args.OriginalSource as FrameworkElement;
if (element == null)
{
return;
}
//args.TranslationBehavior.DesiredDeceleration = 40.0 * 96.0 / (1000.0 * 1000.0);
args.TranslationBehavior.DesiredDeceleration = 20.0 * 96.0 / (1000.0 * 1000.0);
//args.TranslationBehavior.DesiredDeceleration = 0;
args.Handled = true;
}
private void ParentCanvasOnManipulationCompleted(object sender, ManipulationCompletedEventArgs manipulationCompletedEventArgs)
{
CurrentHorizontalBounce = HorizontalEdgeBounceType.None;
CurrentVerticalBounce = VerticalEdgeBounceType.None;
}
private void ParentCanvasOnManipulationDelta(object sender, ManipulationDeltaEventArgs args)
{
var element = args.OriginalSource as FrameworkElement;
if (element != null)
{
if (args.IsInertial)
{
HandleHorizontalBounce(args);
HandleVerticalBounce(args);
args.Handled = true;
return;
}
var currentLeft = Canvas.GetLeft(this);
Canvas.SetLeft(this, currentLeft + args.DeltaManipulation.Translation.X);
var currentTop = Canvas.GetTop(this);
Canvas.SetTop(this, currentTop + args.DeltaManipulation.Translation.Y);
args.Handled = true;
}
}
private void ParentCanvasOnManipulationStarting(object sender, ManipulationStartingEventArgs args)
{
args.ManipulationContainer = ParentCanvas;
args.Handled = true;
((MainWindow)Window.GetWindow(this)).IsCurrentlyManipulating = true;
((MainWindow)Window.GetWindow(this)).currentlyManipulatedControl = this;
}
public static readonly DependencyProperty ParentCanvasProperty = DependencyProperty.Register(
"ParentCanvas", typeof(Canvas), typeof(BouncingUserControl), new PropertyMetadata(default(Canvas)));
public Canvas ParentCanvas
{
get { return (Canvas)GetValue(ParentCanvasProperty); }
set { SetValue(ParentCanvasProperty, value); }
}
private void HandleVerticalBounce(ManipulationDeltaEventArgs args)
{
var currentTop = Canvas.GetTop(this);
//Update the current Vertical bounce if necessary
if (currentTop < 0)
{
CurrentVerticalBounce = VerticalEdgeBounceType.Top;
}
else if (currentTop + ActualHeight >= ParentCanvas.ActualHeight)
{
CurrentVerticalBounce = VerticalEdgeBounceType.Bottom;
}
if (CurrentVerticalBounce != VerticalEdgeBounceType.None)
{
//Apply the new Translation taking into account we have bounced vertically
switch (CurrentVerticalBounce)
{
case VerticalEdgeBounceType.Top:
Canvas.SetTop(this, currentTop + Math.Abs(args.DeltaManipulation.Translation.Y));
break;
case VerticalEdgeBounceType.Bottom:
Canvas.SetTop(this, currentTop - Math.Abs(args.DeltaManipulation.Translation.Y));
break;
}
}
else
{
//Just apply the Translation as normal
Canvas.SetTop(this, currentTop + args.DeltaManipulation.Translation.Y);
}
}
private void HandleHorizontalBounce(ManipulationDeltaEventArgs args)
{
var currentLeft = Canvas.GetLeft(this);
//Update the current Horizontal bounce if necessary
if (currentLeft < 0)
{
CurrentHorizontalBounce = HorizontalEdgeBounceType.Left;
}
else if (currentLeft + ActualWidth >= ParentCanvas.ActualWidth)
{
CurrentHorizontalBounce = HorizontalEdgeBounceType.Right;
}
if (CurrentHorizontalBounce != HorizontalEdgeBounceType.None)
{
//Apply the new Translation taking into account we have bounced horizontally
switch (CurrentHorizontalBounce)
{
case HorizontalEdgeBounceType.Left:
Canvas.SetLeft(this, currentLeft + Math.Abs(args.DeltaManipulation.Translation.X));
break;
case HorizontalEdgeBounceType.Right:
Canvas.SetLeft(this, currentLeft - Math.Abs(args.DeltaManipulation.Translation.X));
break;
}
}
else
{
//Just apply the Translation as normal
Canvas.SetLeft(this, currentLeft + args.DeltaManipulation.Translation.X);
}
}
}
}
这个BouncingUserControl的容器是一个简单的画布:
<Window x:Class="Slingshot.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:Slingshot"
Title="MainWindow" Height="350" Width="525">
<Grid>
<Canvas x:Name="canvas" Background="#0a6c03" TouchDown="canvas_TouchDown" TouchMove="canvas_TouchMove" TouchUp="canvas_TouchUp">
<local:BouncingUserControl ParentCanvas="{Binding ElementName=canvas}" IsManipulationEnabled="True" Canvas.Top="50" Canvas.Left="50" />
</Canvas>
<TextBlock x:Name="indicator" HorizontalAlignment="Left" VerticalAlignment="Top"/>
</Grid>
</Window>
使用此代码隐藏:
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;
namespace Slingshot
{
/// <summary>
/// Logique d'interaction pour MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
public BouncingUserControl currentlyManipulatedControl;
public Boolean IsCurrentlyManipulating = false;
Line slingshotAim;
public MainWindow()
{
InitializeComponent();
InitializeSlingShot();
}
private void InitializeSlingShot()
{
slingshotAim = new Line();
slingshotAim.Stroke = Brushes.LightSteelBlue;
slingshotAim.StrokeThickness = 3;
canvas.Children.Add(slingshotAim);
slingshotAim.Visibility = Visibility.Hidden;
}
private void canvas_TouchDown(object sender, TouchEventArgs e)
{
Console.WriteLine(canvas.TouchesOver.Count());
if (IsCurrentlyManipulating && canvas.TouchesOver.Count() == 2)
{
slingshotAim.Visibility = Visibility.Visible;
slingshotAim.X1 = canvas.TouchesOver.ElementAt(0).GetTouchPoint(canvas).Position.X;
slingshotAim.Y1 = canvas.TouchesOver.ElementAt(0).GetTouchPoint(canvas).Position.Y;
slingshotAim.X2 = canvas.TouchesOver.ElementAt(1).GetTouchPoint(canvas).Position.X;
slingshotAim.Y2 = canvas.TouchesOver.ElementAt(1).GetTouchPoint(canvas).Position.Y;
}
}
private void canvas_TouchUp(object sender, TouchEventArgs e)
{
if (IsCurrentlyManipulating && canvas.TouchesOver.Count() == 2)
{
double slingshotLength = Math.Sqrt(Math.Pow((slingshotAim.X2-slingshotAim.X1),2)+Math.Pow((slingshotAim.Y2-slingshotAim.Y1),2));
indicator.Text = slingshotLength.ToString();
slingshotAim.Visibility = Visibility.Hidden;
}
}
private void canvas_TouchMove(object sender, TouchEventArgs e)
{
if (IsCurrentlyManipulating && canvas.TouchesOver.Count() == 2)
{
double slingshotLength = Math.Sqrt(Math.Pow((slingshotAim.X2 - slingshotAim.X1), 2) + Math.Pow((slingshotAim.Y2 - slingshotAim.Y1), 2));
indicator.Text = slingshotLength.ToString();
slingshotAim.X1 = canvas.TouchesOver.ElementAt(0).GetTouchPoint(canvas).Position.X;
slingshotAim.Y1 = canvas.TouchesOver.ElementAt(0).GetTouchPoint(canvas).Position.Y;
slingshotAim.X2 = canvas.TouchesOver.ElementAt(1).GetTouchPoint(canvas).Position.X;
slingshotAim.Y2 = canvas.TouchesOver.ElementAt(1).GetTouchPoint(canvas).Position.Y;
}
}
}
}
我尝试过一个事件,我会传递一个ManipulationEventArgs,但是没有构造函数。另一种解决问题的方法是在Canvas.TopProperty和Canvas.LeftProperty上使用动画,但是这会阻止我使用水平和垂直反弹,所以我宁愿不这样做。
感谢您提供任何帮助或提示。