您好,登錄后才能下訂單哦!
今天就跟大家聊聊有關如何用Silverlight開發貪吃蛇游戲,可能很多人都不太了解,為了讓大家更加了解,小編給大家總結了以下內容,希望大家根據這篇文章可以有所收獲。
介紹
使用 Silverlight 3.0(c#) 開發一個貪吃蛇游戲
玩法
W/S/A/D 或 ↑/↓/←/→ 控制蛇的移動
截圖
思路
1、貪吃蛇的每一段為 16×16 像素,場景為 640×480 像素,也就說網格為 40×30 個,每個網格的邊長為 16
2、食物的出現位置以及貪吃蛇的運動方向的改變都要在相關的網格內進行
3、貪吃蛇的運動用即時運算的方法計算,當貪吃蛇運動到網格內(蛇某一段的像素位置%網格的邊長<蛇在某時間單位下的移動偏移量)時做如下工作:修正蛇的位置使其正好在網格內,更新蛇的每一段的運動方向,判斷是否吃到了食物、是否發生了碰撞等
4、貪吃蛇的每一段的運動方向的修改:蛇頭的運動方向根據用戶的操作改變,蛇的每一段的運動方向設置為此段的前一段的運動方向(計算時要從尾部向頭部逐段計算)(注:運動方向的改變要在蛇移動到網格內時進行。其中如果蛇的某一段移動到了網格內,則表明其它各段都在網格內)
下面我們看看他的關鍵代碼都有哪些。
關鍵代碼
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; // 網格列數 private int _rows; // 網格行數 private Dictionary<Body, CellPoint> _bodies = new Dictionary<Body, CellPoint>(); // 貪吃蛇每一段的集合 private Dictionary<Bean, CellPoint> _beans = new Dictionary<Bean, CellPoint>(); // 豆的集合 private Dictionary<Skin, CellPoint> _skins = new Dictionary<Skin, CellPoint>(); // 蛻下來的皮的集合 private List<CellPoint> _emptyCells = new List<CellPoint>(); // 空網格的集合 private bool _enabled = false; // 游戲是否運行 private double _dt = 0.01; // 多少毫秒計算一次 private int _decimals = 1; // 計算小數時所保留的小數位 private double _speed = 80; // 蛇的運行速度 private Direction _moveDirection = Direction.Up; // 蛇的運行方向 private int _selfLength = 5; // 蛇的最小長度 private int _beansCount = 5; // 豆的***出現數量 private int _ateCapacity = 10; // 食量(超過則蛻皮) private bool _needRaiseAteEvent = false; // 在“蛇頭所處位置進入了網格點區域內”時是否需要觸發吃豆事件 private int _needBeansCount = 0; // 還需要增加的豆的數量 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); } /// <summary> /// 初始化 /// </summary> 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(); } /// <summary> /// 蛇的初始化 /// </summary> private void InitSnake() { InitHead(); for (int i = 0; i < _selfLength - 1; i++) AddTail(); for (int i = 0; i < _beansCount; i++) AddBean(); } /// <summary> /// 蛇頭的初始化 /// </summary> 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); } /// <summary> /// 增加一個尾巴 /// </summary> 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); } /// <summary> /// 增加一個豆 /// </summary> private void AddBean() { if (_needBeansCount < _beansCount) _needBeansCount++; } private DateTime _prevAddBeanDateTime = DateTime.Now; /// <summary> /// 生成豆 /// </summary> void UpdateBean() { if (_needBeansCount > 0 && (DateTime.Now - _prevAddBeanDateTime).TotalSeconds > 3) { List<CellPoint> emptyCells = 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; } /// <summary> /// 即時計算 /// </summary> private void Update() { if (!_enabled) return; double offset = Math.Round(_speed * _dt, _decimals); // 蛇頭所處位置進入了網格點區域內 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(); } /// <summary> /// 蛻皮 /// </summary> private void CheckSkin() { if (_bodies.Count >= _ateCapacity + _selfLength) AddSkin(_ateCapacity); } /// <summary> /// 碰撞檢測 /// </summary> 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); } } /// <summary> /// 吃豆 /// </summary> 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; } } /// <summary> /// 更新蛇的每一段的運動方向 /// </summary> 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; } } /// <summary> /// 更新蛇的每一段的位置 /// </summary> 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)); } } /// <summary> /// 蛻指定數量的皮 /// </summary> private void AddSkin(int count) { player.PlaySkin(); while (count > 0) { KeyValuePair<Body, CellPoint> 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 輔助方法 /// <summary> /// 修正指定的位置信息為整數 /// </summary> 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; } /// <summary> /// 修正蛇的每一段的位置為整數 /// </summary> 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); } } /// <summary> /// 更新蛇的每一段的網格位置信息 /// </summary> private void UpdateBodyCell() { for (int i = 0; i < _bodies.Count; i++) { UpdateBodyCell(_bodies.ElementAt(i).Key); } } /// <summary> /// 更新指定的 Body 的網格位置信息 /// </summary> 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; } /// <summary> /// 修正網格位置 /// </summary> 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; } /// <summary> /// 獲取空網格集合 /// </summary> private List<CellPoint> GetEmptyCells() { List<CellPoint> emptyCells = new List<CellPoint>(); List<CellPoint> aroundHeadCells = new List<CellPoint>(); 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 } }
看完上述內容,你們對如何用Silverlight開發貪吃蛇游戲有進一步的了解嗎?如果還想了解更多知識或者相關內容,請關注億速云行業資訊頻道,感謝大家的支持。
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。