游戲人生Silverlight:貪吃蛇
介紹
使用 Silverlight 3.0(c#) 開發(fā)一個貪吃蛇游戲
玩法
W/S/A/D 或 ↑/↓/←/→ 控制蛇的移動
截圖
思路
1、貪吃蛇的每一段為 16×16 像素,場景為 640×480 像素,也就說網(wǎng)格為 40×30 個,每個網(wǎng)格的邊長為 16
2、食物的出現(xiàn)位置以及貪吃蛇的運動方向的改變都要在相關(guān)的網(wǎng)格內(nèi)進行
3、貪吃蛇的運動用即時運算的方法計算,當(dāng)貪吃蛇運動到網(wǎng)格內(nèi)(蛇某一段的像素位置%網(wǎng)格的邊長<蛇在某時間單位下的移動偏移量)時做如下工作:修正蛇的位置使其正好在網(wǎng)格內(nèi),更新蛇的每一段的運動方向,判斷是否吃到了食物、是否發(fā)生了碰撞等
4、貪吃蛇的每一段的運動方向的修改:蛇頭的運動方向根據(jù)用戶的操作改變,蛇的每一段的運動方向設(shè)置為此段的前一段的運動方向(計算時要從尾部向頭部逐段計算)(注:運動方向的改變要在蛇移動到網(wǎng)格內(nèi)時進行。其中如果蛇的某一段移動到了網(wǎng)格內(nèi),則表明其它各段都在網(wǎng)格內(nèi))
下面我們看看他的關(guān)鍵代碼都有哪些。
#p#
關(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<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>(); // 空網(wǎng)格的集合
- private bool _enabled = false; // 游戲是否運行
- private double _dt = 0.01; // 多少毫秒計算一次
- private int _decimals = 1; // 計算小數(shù)時所保留的小數(shù)位
- private double _speed = 80; // 蛇的運行速度
- private Direction _moveDirection = Direction.Up; // 蛇的運行方向
- private int _selfLength = 5; // 蛇的最小長度
- private int _beansCount = 5; // 豆的***出現(xiàn)數(shù)量
- private int _ateCapacity = 10; // 食量(超過則蛻皮)
- private bool _needRaiseAteEvent = false; // 在“蛇頭所處位置進入了網(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);
- }
- /// <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);
- // 蛇頭所處位置進入了網(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();
- }
- /// <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>
- /// 蛻指定數(shù)量的皮
- /// </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>
- /// 修正指定的位置信息為整數(shù)
- /// </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>
- /// 修正蛇的每一段的位置為整數(shù)
- /// </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>
- /// 更新蛇的每一段的網(wǎng)格位置信息
- /// </summary>
- private void UpdateBodyCell()
- {
- for (int i = 0; i < _bodies.Count; i++)
- {
- UpdateBodyCell(_bodies.ElementAt(i).Key);
- }
- }
- /// <summary>
- /// 更新指定的 Body 的網(wǎng)格位置信息
- /// </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>
- /// 修正網(wǎng)格位置
- /// </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>
- /// 獲取空網(wǎng)格集合
- /// </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
- }
- }
原文鏈接:http://www.cnblogs.com/webabcd/archive/2009/09/14/1566082.html