今天就跟大家聊聊有關(guān)如何用Silverlight開發(fā)貪吃蛇游戲,可能很多人都不太了解,為了讓大家更加了解,小編給大家總結(jié)了以下內(nèi)容,希望大家根據(jù)這篇文章可以有所收獲。
建網(wǎng)站原本是網(wǎng)站策劃師、網(wǎng)絡(luò)程序員、網(wǎng)頁設(shè)計師等,應(yīng)用各種網(wǎng)絡(luò)程序開發(fā)技術(shù)和網(wǎng)頁設(shè)計技術(shù)配合操作的協(xié)同工作。成都創(chuàng)新互聯(lián)公司專業(yè)提供網(wǎng)站建設(shè)、成都網(wǎng)站設(shè)計,網(wǎng)頁設(shè)計,網(wǎng)站制作(企業(yè)站、響應(yīng)式網(wǎng)站設(shè)計、電商門戶網(wǎng)站)等服務(wù),從網(wǎng)站深度策劃、搜索引擎友好度優(yōu)化到用戶體驗的提升,我們力求做到極致!
介紹
使用 Silverlight 3.0(c#) 開發(fā)一個貪吃蛇游戲
玩法
W/S/A/D 或 ↑/↓/←/→ 控制蛇的移動
截圖
思路
1、貪吃蛇的每一段為 16×16 像素,場景為 640×480 像素,也就說網(wǎng)格為 40×30 個,每個網(wǎng)格的邊長為 16
2、食物的出現(xiàn)位置以及貪吃蛇的運(yùn)動方向的改變都要在相關(guān)的網(wǎng)格內(nèi)進(jìn)行
3、貪吃蛇的運(yùn)動用即時運(yùn)算的方法計算,當(dāng)貪吃蛇運(yùn)動到網(wǎng)格內(nèi)(蛇某一段的像素位置%網(wǎng)格的邊長<蛇在某時間單位下的移動偏移量)時做如下工作:修正蛇的位置使其正好在網(wǎng)格內(nèi),更新蛇的每一段的運(yùn)動方向,判斷是否吃到了食物、是否發(fā)生了碰撞等
4、貪吃蛇的每一段的運(yùn)動方向的修改:蛇頭的運(yùn)動方向根據(jù)用戶的操作改變,蛇的每一段的運(yùn)動方向設(shè)置為此段的前一段的運(yùn)動方向(計算時要從尾部向頭部逐段計算)(注:運(yùn)動方向的改變要在蛇移動到網(wǎng)格內(nèi)時進(jìn)行。其中如果蛇的某一段移動到了網(wǎng)格內(nèi),則表明其它各段都在網(wǎng)格內(nèi))
下面我們看看他的關(guān)鍵代碼都有哪些。
關(guān)鍵代碼
using System; using System.Collections.Generic; using System.Linq; using System.Net; using System.Windows; using System.Windows.Controls; using System.Windows.Documents; using System.Windows.Input; using System.Windows.Media; using System.Windows.Media.Animation; using System.Windows.Shapes; using System.Windows.Media.Imaging; using System.Threading; namespace YYSnake.Core { public partial class Main : UserControl { private int _columns; // 網(wǎng)格列數(shù) private int _rows; // 網(wǎng)格行數(shù) private Dictionary _bodies = new Dictionary(); // 貪吃蛇每一段的集合 private Dictionary_beans = new Dictionary (); // 豆的集合 private Dictionary _skins = new Dictionary (); // 蛻下來的皮的集合 private List _emptyCells = new List (); // 空網(wǎng)格的集合 private bool _enabled = false; // 游戲是否運(yùn)行 private double _dt = 0.01; // 多少毫秒計算一次 private int _decimals = 1; // 計算小數(shù)時所保留的小數(shù)位 private double _speed = 80; // 蛇的運(yùn)行速度 private Direction _moveDirection = Direction.Up; // 蛇的運(yùn)行方向 private int _selfLength = 5; // 蛇的最小長度 private int _beansCount = 5; // 豆的***出現(xiàn)數(shù)量 private int _ateCapacity = 10; // 食量(超過則蛻皮) private bool _needRaiseAteEvent = false; // 在“蛇頭所處位置進(jìn)入了網(wǎng)格點區(qū)域內(nèi)”時是否需要觸發(fā)吃豆事件 private int _needBeansCount = 0; // 還需要增加的豆的數(shù)量 Random _random = new Random(); public Main() { InitializeComponent(); this.Loaded += new RoutedEventHandler(Main_Loaded); } void Main_Loaded(object sender, RoutedEventArgs e) { this.Width = App.Width; // 640 this.Height = App.Height; // 480 _columns = (int)(Width / App.CellSize); // 40 _rows = (int)(Height / App.CellSize); // 30 // 防止動畫飛出去 RectangleGeometry rg = new RectangleGeometry(); rg.Rect = new Rect(0, 0, App.Width, App.Height); LayoutRoot.Clip = rg; bg.Width = App.Width; bg.Height = App.Height; ripple.RippleBackground = bg; CompositionTarget.Rendering += new EventHandler(CompositionTarget_Rendering); } /// /// 初始化 /// public void Init() { _enabled = false; canvasBean.Children.Clear(); canvasSnake.Children.Clear(); canvasSkin.Children.Clear(); _beans.Clear(); _bodies.Clear(); _skins.Clear(); _emptyCells.Clear(); for (int i = 0; i < _columns; i++) { for (int j = 0; j < _rows; j++) { _emptyCells.Add(new CellPoint(i, j)); } } _moveDirection = Direction.Up; InitSnake(); } ////// 蛇的初始化 /// private void InitSnake() { InitHead(); for (int i = 0; i < _selfLength - 1; i++) AddTail(); for (int i = 0; i < _beansCount; i++) AddBean(); } ////// 蛇頭的初始化 /// private void InitHead() { Body head = new Body(BodyType.Head); head.MoveDirection = _moveDirection; CellPoint point = new CellPoint((int)(_columns / 2), (int)(_rows / 2)); head.SetValue(Canvas.LeftProperty, point.X * App.CellSize); head.SetValue(Canvas.TopProperty, point.Y * App.CellSize); _bodies.Add(head, point); canvasSnake.Children.Add(head); } ////// 增加一個尾巴 /// private void AddTail() { var prevBody = _bodies.Last().Key; var prevBodyPoint = _bodies.Last().Value; Body tail = new Body(BodyType.Tail); tail.MoveDirection = prevBody.MoveDirection; CellPoint tailPoint = new CellPoint(prevBodyPoint.X, prevBodyPoint.Y); switch (prevBody.MoveDirection) { case Direction.Up: tail.SetValue(Canvas.LeftProperty, (double)prevBody.GetValue(Canvas.LeftProperty)); tail.SetValue(Canvas.TopProperty, (double)prevBody.GetValue(Canvas.TopProperty) + App.CellSize); tailPoint.Y++; break; case Direction.Down: tail.SetValue(Canvas.LeftProperty, (double)prevBody.GetValue(Canvas.LeftProperty)); tail.SetValue(Canvas.TopProperty, (double)prevBody.GetValue(Canvas.TopProperty) - App.CellSize); tailPoint.Y--; break; case Direction.Left: tail.SetValue(Canvas.LeftProperty, (double)prevBody.GetValue(Canvas.LeftProperty) + App.CellSize); tail.SetValue(Canvas.TopProperty, (double)prevBody.GetValue(Canvas.TopProperty)); tailPoint.X++; break; case Direction.Right: tail.SetValue(Canvas.LeftProperty, (double)prevBody.GetValue(Canvas.LeftProperty) - App.CellSize); tail.SetValue(Canvas.TopProperty, (double)prevBody.GetValue(Canvas.TopProperty)); tailPoint.X--; break; } tailPoint = CorrectCellPoint(tailPoint); _bodies.Add(tail, tailPoint); canvasSnake.Children.Add(tail); } ////// 增加一個豆 /// private void AddBean() { if (_needBeansCount < _beansCount) _needBeansCount++; } private DateTime _prevAddBeanDateTime = DateTime.Now; ////// 生成豆 /// void UpdateBean() { if (_needBeansCount > 0 && (DateTime.Now - _prevAddBeanDateTime).TotalSeconds > 3) { ListemptyCells = GetEmptyCells(); if (emptyCells.Count == 0) { GameOver(this, EventArgs.Empty); return; } CellPoint point = emptyCells[_random.Next(0, emptyCells.Count)]; Bean bean = new Bean(); bean.SetValue(Canvas.LeftProperty, point.X * App.CellSize); bean.SetValue(Canvas.TopProperty, point.Y * App.CellSize); _beans.Add(bean, point); canvasBean.Children.Add(bean); bean.ani.Completed += delegate { ripple.ShowRipple(new Point(point.X * App.CellSize + App.CellSize / 2, point.Y * App.CellSize + App.CellSize / 2)); player.PlayDrop(); }; _needBeansCount--; _prevAddBeanDateTime = DateTime.Now; } } private DateTime _prevDateTime = DateTime.Now; private double _leftoverLength = 0d; void CompositionTarget_Rendering(object sender, EventArgs e) { double length = (DateTime.Now - _prevDateTime).TotalSeconds + _leftoverLength; while (length > _dt) { Update(); length -= _dt; } _leftoverLength = length; _prevDateTime = DateTime.Now; } /// /// 即時計算 /// private void Update() { if (!_enabled) return; double offset = Math.Round(_speed * _dt, _decimals); // 蛇頭所處位置進(jìn)入了網(wǎng)格點區(qū)域內(nèi) if (Math.Abs(Math.Round((double)_bodies.First().Key.GetValue(Canvas.TopProperty) % App.CellSize, _decimals)) < offset && Math.Abs(Math.Round((double)_bodies.First().Key.GetValue(Canvas.LeftProperty) % App.CellSize, _decimals)) < offset) { UpdateDirection(); CorrectPosition(); UpdateBodyCell(); CheckEat(); CheckSkin(); CheckCollision(); UpdateBean(); if (_needRaiseAteEvent) { Ate(this.Ate, EventArgs.Empty); _needRaiseAteEvent = false; } } UpdatePosition(); } ////// 蛻皮 /// private void CheckSkin() { if (_bodies.Count >= _ateCapacity + _selfLength) AddSkin(_ateCapacity); } ////// 碰撞檢測 /// private void CheckCollision() { if (_skins.Any(p => p.Value == _bodies.First().Value) || _bodies.Where(p => p.Key.BodyType == BodyType.Tail).Any(p => p.Value == _bodies.First().Value)) { _enabled = false; player.PlayOver(); GameOver(this, EventArgs.Empty); } } ////// 吃豆 /// private void CheckEat() { // 是否有被吃的豆 var bean = _beans.FirstOrDefault(p => p.Value == _bodies.First().Value).Key; if (bean != null) { _beans.Remove(bean); canvasBean.Children.Remove(bean); player.PlayEat(); AddTail(); AddBean(); _needRaiseAteEvent = true; } } ////// 更新蛇的每一段的運(yùn)動方向 /// private void UpdateDirection() { for (int i = _bodies.Count - 1; i > -1; i--) { if (i == 0) _bodies.ElementAt(i).Key.MoveDirection = _moveDirection; else _bodies.ElementAt(i).Key.MoveDirection = _bodies.ElementAt(i - 1).Key.MoveDirection; } } ////// 更新蛇的每一段的位置 /// private void UpdatePosition() { double offset = Math.Round(_speed * _dt, _decimals); foreach (var body in _bodies.Keys) { if (body.MoveDirection == Direction.Up) body.SetValue(Canvas.TopProperty, Math.Round((double)body.GetValue(Canvas.TopProperty) - offset, _decimals)); else if (body.MoveDirection == Direction.Down) body.SetValue(Canvas.TopProperty, Math.Round((double)body.GetValue(Canvas.TopProperty) + offset, _decimals)); else if (body.MoveDirection == Direction.Left) body.SetValue(Canvas.LeftProperty, Math.Round((double)body.GetValue(Canvas.LeftProperty) - offset, _decimals)); else if (body.MoveDirection == Direction.Right) body.SetValue(Canvas.LeftProperty, Math.Round((double)body.GetValue(Canvas.LeftProperty) + offset, _decimals)); } } ////// 蛻指定數(shù)量的皮 /// private void AddSkin(int count) { player.PlaySkin(); while (count > 0) { KeyValuePair body = _bodies.ElementAt(_bodies.Count - 1); CellPoint skinPoint = body.Value; Skin skin = new Skin(); skin.SetValue(Canvas.LeftProperty, skinPoint.X * App.CellSize); skin.SetValue(Canvas.TopProperty, skinPoint.Y * App.CellSize); _skins.Add(skin, skinPoint); canvasSkin.Children.Add(skin); _emptyCells.Remove(skinPoint); canvasSnake.Children.Remove(body.Key); _bodies.Remove(body.Key); count--; } } #region 輔助方法 ////// 修正指定的位置信息為整數(shù) /// private int CorrectPosition(double position) { double result; double offset = Math.Round(_speed * _dt, _decimals); double temp = Math.Round(position % App.CellSize, _decimals); if (Math.Abs(temp) < offset) result = Math.Round(position - temp); else result = Math.Round(position - temp) + Math.Sign(temp) * App.CellSize; return (int)result; } ////// 修正蛇的每一段的位置為整數(shù) /// private void CorrectPosition() { foreach (Body body in _bodies.Keys) { double x = CorrectPosition((double)body.GetValue(Canvas.LeftProperty)); double y = CorrectPosition((double)body.GetValue(Canvas.TopProperty)); if (x == App.Width) x = 0d; else if (x == -App.CellSize) x = App.Width - App.CellSize; else if (y == App.Height) y = 0d; else if (y == -App.CellSize) y = App.Height - App.CellSize; body.SetValue(Canvas.LeftProperty, x); body.SetValue(Canvas.TopProperty, y); } } ////// 更新蛇的每一段的網(wǎng)格位置信息 /// private void UpdateBodyCell() { for (int i = 0; i < _bodies.Count; i++) { UpdateBodyCell(_bodies.ElementAt(i).Key); } } ////// 更新指定的 Body 的網(wǎng)格位置信息 /// private void UpdateBodyCell(Body body) { CellPoint point = new CellPoint((int)((double)body.GetValue(Canvas.LeftProperty) / App.CellSize), (int)((double)body.GetValue(Canvas.TopProperty) / App.CellSize)); if (body.MoveDirection == Direction.Up) point.Y--; else if (body.MoveDirection == Direction.Down) point.Y++; else if (body.MoveDirection == Direction.Left) point.X--; else if (body.MoveDirection == Direction.Right) point.X++; point = CorrectCellPoint(point); _bodies[body] = point; } ////// 修正網(wǎng)格位置 /// private CellPoint CorrectCellPoint(CellPoint point) { if (point.X > _columns - 1) point.X = _columns - point.X; else if (point.X < 0) point.X = point.X + _columns; if (point.Y > _rows - 1) point.Y = _rows - point.Y; else if (point.Y < 0) point.Y = point.Y + _rows; return point; } ////// 獲取空網(wǎng)格集合 /// private ListGetEmptyCells() { List emptyCells = new List (); List aroundHeadCells = new List (); CellPoint headPoint = _bodies.First().Value; for (int i = -5; i < 5; i++) { for (int j = -5; j < 5; j++) { CellPoint point = new CellPoint(headPoint.X + i, headPoint.Y + j); point = CorrectCellPoint(point); aroundHeadCells.Add(point); } } // skin 的占位情況因為確定了就不變了,所以在 AddSkin() 處計算 // 為了以下 LINQ 的可用,需要重寫 CellPoint 的 public override bool Equals(object obj) emptyCells = _emptyCells.Where(p => !_bodies.Select(x => x.Value).Contains(p)).ToList(); emptyCells = emptyCells.Where(p => !_beans.Select(x => x.Value).Contains(p)).ToList(); emptyCells = emptyCells.Where(p => !aroundHeadCells.Contains(p)).ToList(); return emptyCells; } #endregion #region 屬性 public Direction MoveDirection { set { Body head = _bodies.First().Key; if (head.MoveDirection == Direction.Up && value == Direction.Down) return; if (head.MoveDirection == Direction.Down && value == Direction.Up) return; if (head.MoveDirection == Direction.Left && value == Direction.Right) return; if (head.MoveDirection == Direction.Right && value == Direction.Left) return; _moveDirection = value; } } public bool Enabled { get { return _enabled; } set { _enabled = value; } } public double Speed { get { return _speed; } set { _speed = value; } } public int AteCapacity { get { return _ateCapacity; } set { _ateCapacity = value; } } #endregion #region 事件 GameOver 和 Ate public event EventHandler GameOver; public event EventHandler Ate; #endregion } }
看完上述內(nèi)容,你們對如何用Silverlight開發(fā)貪吃蛇游戲有進(jìn)一步的了解嗎?如果還想了解更多知識或者相關(guān)內(nèi)容,請關(guān)注創(chuàng)新互聯(lián)行業(yè)資訊頻道,感謝大家的支持。