且构网

分享程序员开发的那些事...
且构网 - 分享程序员编程开发的那些事

Silverlight 游戏开发:可重用的拖拽控件

更新时间:2022-10-01 11:05:33

游戏中有各种各样的拖拽需求,大到窗口,小到图标,在游戏界面操作中,点击和拖拽占据了用户操作的大部分行为,如何做好一个拖拽控件至关重要,做一个可重用的拖拽控件更加重要,我的这些实现方法可能比较另类,但只要有效就行,在这个基础上,你可以扩展很多的做法。

可能有朋友已经写了这方面的文章,但是本篇介绍的方法是一个可以一劳永逸的重用控件,只需要一个基类代码就可以完成所有的需求——图标、窗体、自定义的目标,所以,本片没有放在小技巧里而是游戏开发分类里。

最先,需要了解一下拖拽原理,即当鼠标按下做一个标识,在鼠标移动时实时修改目标坐标信息,鼠标抬起的时候,释放掉鼠标操作,当然了,为了更好的操作坐标,我们一般将父容器改成Canvas。

 

Silverlight 游戏开发:可重用的拖拽控件

将围绕这个做后面的工作,请了解之前有关基类和容器的概念,这样后面看起来就容易很多,考虑它的重用性,创建一个基本控件让后面的控件继承,将一些通用逻辑写在这个基类里,让其他的类去继承重用,这也就是游戏引擎的基本做法之一。

那么,在Blend或者Visual Studio里创建一个名为MovableObject的控件,然后将控件xaml修改成下面的样子:

 

  1. <UserControl    
  2.  
  3. 02 xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"   
  4.  
  5. 03 xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"   
  6.  
  7. 04 xmlns:d="http://schemas.microsoft.com/expression/blend/2008"   
  8.  
  9. 05 xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"   
  10.  
  11. 06 mc:Ignorable="d"   
  12.  
  13. 07 x:Class="DragObject.MovableObject"   
  14.  
  15. 08 d:DesignWidth="640" d:DesignHeight="480" Width="Auto" Height="Auto">    
  16.  
  17. 09 <Grid x:Name="LayoutRoot">    
  18.  
  19. 10 <Rectangle Stroke="Black" RadiusX="5" RadiusY="5">    
  20.  
  21. 11 <Rectangle.Fill>    
  22.  
  23. 12 <LinearGradientBrush EndPoint="0.5,1" StartPoint="0.5,0">    
  24.  
  25. 13 <GradientStop Color="Transparent"/>    
  26.  
  27. 14 <GradientStop Color="Black" Offset="1"/>    
  28.  
  29. 15 </LinearGradientBrush>    
  30.  
  31. 16 </Rectangle.Fill>    
  32.  
  33. 17 </Rectangle>    
  34.  
  35. 18 <Image x:Name="ShowImage" Stretch="Fill"/>    
  36.  
  37. 19 <Rectangle x:Name="Sel_Rectangle" Stroke="White" StrokeThickness="2" Visibility="Collapsed" RadiusX="5" RadiusY="5"/>    
  38.  
  39. 20 </Grid>    
  40.  
  41. 21 </UserControl>   
即便使用代码实现并不难,但是为了方便理解,就直接使用XAML较为简便,浏览它的结构图就可以看到。

 

Silverlight 游戏开发:可重用的拖拽控件

上面的表示方法分别是带了一个底,和一个要显示图像的Image,以及一个当鼠标移入时候需要表示选择标识。

那么下面就是Coding阶段,打开MovableObject类,代码设计如下:


  1. public partial class MovableObject : UserControl    
  2.  
  3. 02 {    
  4.  
  5. 03 //鼠标点的保存,同时还承担是否点击的判定    
  6.  
  7. 04 Point ? mousePoint = null;    
  8.  
  9. 05 public MovableObject()    
  10.  
  11. 06 {    
  12.  
  13. 07 InitializeComponent();    
  14.  
  15. 08 //选择框隐蔽掉    
  16.  
  17. 09 Sel_Rectangle.Visibility = System.Windows.Visibility.Collapsed;    
  18.  
  19. 10 }    
  20.  
  21. 11 //鼠标移动的方法重载    
  22.  
  23. 12 protected override void OnMouseMove(MouseEventArgs e)    
  24.  
  25. 13 {    
  26.  
  27. 14 //判定是否按下鼠标左键    
  28.  
  29. 15 if (mousePoint!=null)    
  30.  
  31. 16 {    
  32.  
  33. 17 //计算新的位置    
  34.  
  35. 18 double newTop = e.GetPosition(null).Y - mousePoint.Value.Y + Canvas.GetTop(this);    
  36.  
  37. 19 double newLeft = e.GetPosition(null).X - mousePoint.Value.X + Canvas.GetLeft(this);    
  38.  
  39. 20 Canvas.SetTop(this, newTop);    
  40.  
  41. 21 Canvas.SetLeft(this, newLeft);    
  42.  
  43. 22 mousePoint = e.GetPosition(null);    
  44.  
  45. 23 }    
  46.  
  47. 24 base.OnMouseMove(e);    
  48.  
  49. 25 }    
  50.  
  51. 26 //鼠标抬起的方法重载    
  52.  
  53. 27 protected override void OnMouseLeftButtonUp(MouseButtonEventArgs e)    
  54.  
  55. 28 {    
  56.  
  57. 29 mousePoint = null;    
  58.  
  59. 30 //释放鼠标设备    
  60.  
  61. 31 this.ReleaseMouseCapture();    
  62.  
  63. 32 base.OnMouseLeftButtonDown(e);    
  64.  
  65. 33 }    
  66.  
  67. 34 //鼠标按下的方法重载    
  68.  
  69. 35 protected override void OnMouseLeftButtonDown(MouseButtonEventArgs e)    
  70.  
  71. 36 {    
  72.  
  73. 37 mousePoint = e.GetPosition(null);    
  74.  
  75. 38 //捕获鼠标设备    
  76.  
  77. 39 this.CaptureMouse();    
  78.  
  79. 40 //下面三行是用来保证当前控件为最顶层的做法    
  80.  
  81. 41 var parent = this.Parent as Panel;    
  82.  
  83. 42 parent.Children.Remove(this);    
  84.  
  85. 43 parent.Children.Add(this);    
  86.  
  87. 44 base.OnMouseLeftButtonUp(e);    
  88.  
  89. 45 }    
  90.  
  91. 46 //鼠标进入的方法重载    
  92.  
  93. 47 protected override void OnMouseEnter(MouseEventArgs e)    
  94.  
  95. 48 {    
  96.  
  97. 49 Sel_Rectangle.Visibility = System.Windows.Visibility.Visible;    
  98.  
  99. 50 base.OnMouseEnter(e);    
  100.  
  101. 51 }    
  102.  
  103. 52 //鼠标移出的方法重载    
  104.  
  105. 53 protected override void OnMouseLeave(MouseEventArgs e)    
  106.  
  107. 54 {    
  108.  
  109. 55 Sel_Rectangle.Visibility = System.Windows.Visibility.Collapsed;    
  110.  
  111. 56 base.OnMouseLeave(e);    
  112.  
  113. 57 }    
  114.  
  115. 58 }   
我做了一些注释,可以很明确的得知这个基类的作用,现在可以新建类继承于这个类,来实现扩展的目的。

为此,我准备三种不同的目标效果——图标(MyIcon)、大图片(MyFace)、自定义控件(MyCard)

图标和图片只需要用上原有控件的Image即可,而自定义控件则是通过Blend或其他方式设计制作出来的独立控件,那么如何实现这三个效果呢?请往下看:

我们先创建三个类,他们都继承于MovableObject

public class MyIcon : MovableObject

public class MyFace : MovableObject

public class MyCard : MovableObject

下面在各自的构造函数中填入对应的操作逻辑即可,下面给出了完整代码:


  1. public class MyIcon : MovableObject    
  2.  
  3. 02 {    
  4.  
  5. 03 public MyIcon()    
  6.  
  7. 04 {    
  8.  
  9. 05 IconIndex = 1;    
  10.  
  11. 06 }    
  12.  
  13. 07 private int _Iconindex = -1;    
  14.  
  15. 08 public int IconIndex    
  16.  
  17. 09 {    
  18.  
  19. 10 get { return _Iconindex; }    
  20.  
  21. 11 set   
  22.  
  23. 12 {    
  24.  
  25. 13 _Iconindex = value;    
  26.  
  27. 14 var uri = new Uri("/DragObject;component/Res/image" + value + ".png", UriKind.Relative);    
  28.  
  29. 15 ShowImage.Source = new System.Windows.Media.Imaging.BitmapImage(uri);    
  30.  
  31. 16 }    
  32.  
  33. 17 }    
  34.  
  35. 18 }    
  36.  
  37. 19 public class MyFace : MovableObject    
  38.  
  39. 20 {    
  40.  
  41. 21 public MyFace()    
  42.  
  43. 22 {    
  44.  
  45. 23 var uri = new Uri("/DragObject;component/Res/nowpaper.jpg", UriKind.Relative);    
  46.  
  47. 24 ShowImage.Source = new System.Windows.Media.Imaging.BitmapImage(uri);   
  48. 01 }    
  49.  
  50. 02 }    
  51.  
  52. 03 public class MyCard : MovableObject    
  53.  
  54. 04 {    
  55.  
  56. 05 public MyCard()    
  57.  
  58. 06 {    
  59.  
  60. 07 LayoutRoot.Children.Insert(LayoutRoot.Children.IndexOf(ShowImage), new Card());    
  61.  
  62. 08 LayoutRoot.Children.Remove(ShowImage);    
  63.  
  64. 09 }    
  65.  
  66. 10 }   
MyFace是直接修改ShowImage的Source,MyIcon里写了一个索引属性,这样可以在外面控制ShowImage,显示需要显示的图片,而MyCard里new出来一个控件,并替换掉了原有的ShowImage,MyCard使用了前面制作的一个控件。

好了,现在在MainPage的构造函数中编写其他的代码,但是LayoutRoot需要变成Canvas,这样才能更好的控制图像位置。


  1. public MainPage()    
  2.  
  3. 02 {    
  4.  
  5. 03 InitializeComponent();    
  6.  
  7. 04 //添加自定义的拖拽目标    
  8.  
  9. 05 var icon = new MyIcon();    
  10.  
  11. 06 LayoutRoot.Children.Add(icon);    
  12.  
  13. 07 Canvas.SetLeft(icon, 20);    
  14.  
  15. 08 Canvas.SetTop(icon, 50);    
  16.  
  17. 09 icon = new MyIcon() { IconIndex = 3 };    
  18.  
  19. 10 LayoutRoot.Children.Add(icon);    
  20.  
  21. 11 Canvas.SetLeft(icon, 20);    
  22.  
  23. 12 Canvas.SetTop(icon, 150);    
  24.  
  25. 13 var face = new MyFace();    
  26.  
  27. 14 LayoutRoot.Children.Add(face);    
  28.  
  29. 15 Canvas.SetLeft(face, 100);    
  30.  
  31. 16 Canvas.SetTop(face, 50);    
  32.  
  33. 17 var card = new MyCard();    
  34.  
  35. 18 LayoutRoot.Children.Add(card);    
  36.  
  37. 19 Canvas.SetLeft(card, 270);    
  38.  
  39. 20 Canvas.SetTop(card, 50);    
  40.  
  41. 21 }   
现在运行看看效果吧,在这个基础上,可以制作窗口或需要拖拽的物体只需要继承修改一下,在下一篇,将会制作一个窗口并使用技巧完成拖入等操作,放心吧绝对不复杂。

本篇工程源代码下载地址如下:点击直接下载



本文转自nowpaper 51CTO博客,原文链接:http://blog.51cto.com/nowpaper/712743