天天看點

Silverlight 遊戲開發:可重用的拖拽控件

遊戲中有各種各樣的拖拽需求,大到視窗,小到圖示,在遊戲界面操作中,點選和拖拽占據了使用者操作的大部分行為,如何做好一個拖拽控件至關重要,做一個可重用的拖拽控件更加重要,我的這些實作方法可能比較另類,但隻要有效就行,在這個基礎上,你可以擴充很多的做法。

可能有朋友已經寫了這方面的文章,但是本篇介紹的方法是一個可以一勞永逸的重用控件,隻需要一個基類代碼就可以完成所有的需求——圖示、窗體、自定義的目标,是以,本片沒有放在小技巧裡而是遊戲開發分類裡。

最先,需要了解一下拖拽原理,即當滑鼠按下做一個辨別,在滑鼠移動時實時修改目标坐标資訊,滑鼠擡起的時候,釋放掉滑鼠操作,當然了,為了更好的操作坐标,我們一般将父容器改成Canvas。

<a target="_blank" href="http://blog.51cto.com/attachment/201111/204027708.jpg"></a>

将圍繞這個做後面的工作,請了解之前有關基類和容器的概念,這樣後面看起來就容易很多,考慮它的重用性,建立一個基本控件讓後面的控件繼承,将一些通用邏輯寫在這個基類裡,讓其他的類去繼承重用,這也就是遊戲引擎的基本做法之一。

那麼,在Blend或者Visual Studio裡建立一個名為MovableObject的控件,然後将控件xaml修改成下面的樣子:

&lt;UserControl    

02 xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"   

03 xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"   

04 xmlns:d="http://schemas.microsoft.com/expression/blend/2008"   

05 xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"   

06 mc:Ignorable="d"   

07 x:Class="DragObject.MovableObject"   

08 d:DesignWidth="640" d:DesignHeight="480" Width="Auto" Height="Auto"&gt;    

09 &lt;Grid x:Name="LayoutRoot"&gt;    

10 &lt;Rectangle Stroke="Black" RadiusX="5" RadiusY="5"&gt;    

11 &lt;Rectangle.Fill&gt;    

12 &lt;LinearGradientBrush EndPoint="0.5,1" StartPoint="0.5,0"&gt;    

13 &lt;GradientStop Color="Transparent"/&gt;    

14 &lt;GradientStop Color="Black" Offset="1"/&gt;    

15 &lt;/LinearGradientBrush&gt;    

16 &lt;/Rectangle.Fill&gt;    

17 &lt;/Rectangle&gt;    

18 &lt;Image x:Name="ShowImage" Stretch="Fill"/&gt;    

19 &lt;Rectangle x:Name="Sel_Rectangle" Stroke="White" StrokeThickness="2" Visibility="Collapsed" RadiusX="5" RadiusY="5"/&gt;    

20 &lt;/Grid&gt;    

21 &lt;/UserControl&gt;   

即便使用代碼實作并不難,但是為了友善了解,就直接使用XAML較為簡便,浏覽它的結構圖就可以看到。

<a target="_blank" href="http://blog.51cto.com/attachment/201111/204134655.jpg"></a>

上面的表示方法分别是帶了一個底,和一個要顯示圖像的Image,以及一個當滑鼠移入時候需要表示選擇辨別。

那麼下面就是Coding階段,打開MovableObject類,代碼設計如下:

public partial class MovableObject : UserControl    

02 {    

03 //滑鼠點的儲存,同時還承擔是否點選的判定    

04 Point ? mousePoint = null;    

05 public MovableObject()    

06 {    

07 InitializeComponent();    

08 //選擇框隐蔽掉    

09 Sel_Rectangle.Visibility = System.Windows.Visibility.Collapsed;    

10 }    

11 //滑鼠移動的方法重載    

12 protected override void OnMouseMove(MouseEventArgs e)    

13 {    

14 //判定是否按下滑鼠左鍵    

15 if (mousePoint!=null)    

16 {    

17 //計算新的位置    

18 double newTop = e.GetPosition(null).Y - mousePoint.Value.Y + Canvas.GetTop(this);    

19 double newLeft = e.GetPosition(null).X - mousePoint.Value.X + Canvas.GetLeft(this);    

20 Canvas.SetTop(this, newTop);    

21 Canvas.SetLeft(this, newLeft);    

22 mousePoint = e.GetPosition(null);    

23 }    

24 base.OnMouseMove(e);    

25 }    

26 //滑鼠擡起的方法重載    

27 protected override void OnMouseLeftButtonUp(MouseButtonEventArgs e)    

28 {    

29 mousePoint = null;    

30 //釋放滑鼠裝置    

31 this.ReleaseMouseCapture();    

32 base.OnMouseLeftButtonDown(e);    

33 }    

34 //滑鼠按下的方法重載    

35 protected override void OnMouseLeftButtonDown(MouseButtonEventArgs e)    

36 {    

37 mousePoint = e.GetPosition(null);    

38 //捕獲滑鼠裝置    

39 this.CaptureMouse();    

40 //下面三行是用來保證目前控件為最頂層的做法    

41 var parent = this.Parent as Panel;    

42 parent.Children.Remove(this);    

43 parent.Children.Add(this);    

44 base.OnMouseLeftButtonUp(e);    

45 }    

46 //滑鼠進入的方法重載    

47 protected override void OnMouseEnter(MouseEventArgs e)    

48 {    

49 Sel_Rectangle.Visibility = System.Windows.Visibility.Visible;    

50 base.OnMouseEnter(e);    

51 }    

52 //滑鼠移出的方法重載    

53 protected override void OnMouseLeave(MouseEventArgs e)    

54 {    

55 Sel_Rectangle.Visibility = System.Windows.Visibility.Collapsed;    

56 base.OnMouseLeave(e);    

57 }    

58 }   

我做了一些注釋,可以很明确的得知這個基類的作用,現在可以建立類繼承于這個類,來實作擴充的目的。

為此,我準備三種不同的目标效果——圖示(MyIcon)、大圖檔(MyFace)、自定義控件(MyCard)

圖示和圖檔隻需要用上原有控件的Image即可,而自定義控件則是通過Blend或其他方式設計制作出來的獨立控件,那麼如何實作這三個效果呢?請往下看:

我們先建立三個類,他們都繼承于MovableObject

public class MyIcon : MovableObject

public class MyFace : MovableObject

public class MyCard : MovableObject

下面在各自的構造函數中填入對應的操作邏輯即可,下面給出了完整代碼:

public class MyIcon : MovableObject    

03 public MyIcon()    

04 {    

05 IconIndex = 1;    

06 }    

07 private int _Iconindex = -1;    

08 public int IconIndex    

09 {    

10 get { return _Iconindex; }    

11 set   

12 {    

13 _Iconindex = value;    

14 var uri = new Uri("/DragObject;component/Res/image" + value + ".png", UriKind.Relative);    

15 ShowImage.Source = new System.Windows.Media.Imaging.BitmapImage(uri);    

16 }    

17 }    

18 }    

19 public class MyFace : MovableObject    

20 {    

21 public MyFace()    

22 {    

23 var uri = new Uri("/DragObject;component/Res/nowpaper.jpg", UriKind.Relative);    

24 ShowImage.Source = new System.Windows.Media.Imaging.BitmapImage(uri);   

01 }    

02 }    

03 public class MyCard : MovableObject    

05 public MyCard()    

07 LayoutRoot.Children.Insert(LayoutRoot.Children.IndexOf(ShowImage), new Card());    

08 LayoutRoot.Children.Remove(ShowImage);    

09 }    

10 }   

MyFace是直接修改ShowImage的Source,MyIcon裡寫了一個索引屬性,這樣可以在外面控制ShowImage,顯示需要顯示的圖檔,而MyCard裡new出來一個控件,并替換掉了原有的ShowImage,MyCard使用了前面制作的一個控件。

好了,現在在MainPage的構造函數中編寫其他的代碼,但是LayoutRoot需要變成Canvas,這樣才能更好的控制圖像位置。

public MainPage()    

03 InitializeComponent();    

04 //添加自定義的拖拽目标    

05 var icon = new MyIcon();    

06 LayoutRoot.Children.Add(icon);    

07 Canvas.SetLeft(icon, 20);    

08 Canvas.SetTop(icon, 50);    

09 icon = new MyIcon() { IconIndex = 3 };    

10 LayoutRoot.Children.Add(icon);    

11 Canvas.SetLeft(icon, 20);    

12 Canvas.SetTop(icon, 150);    

13 var face = new MyFace();    

14 LayoutRoot.Children.Add(face);    

15 Canvas.SetLeft(face, 100);    

16 Canvas.SetTop(face, 50);    

17 var card = new MyCard();    

18 LayoutRoot.Children.Add(card);    

19 Canvas.SetLeft(card, 270);    

20 Canvas.SetTop(card, 50);    

21 }   

現在運作看看效果吧,在這個基礎上,可以制作視窗或需要拖拽的物體隻需要繼承修改一下,在下一篇,将會制作一個視窗并使用技巧完成拖入等操作,放心吧絕對不複雜。

本文轉自nowpaper 51CTO部落格,原文連結:http://blog.51cto.com/nowpaper/712743