遊戲人生Silverlight(6) - 貪吃蛇[Silverlight 3.0(c#)]

 [源碼下載]


遊戲人生Silverlight(6) - 貪吃蛇[Silverlight 3.0(c#)]


作者:webabcd


介紹
使用 Silverlight 3.0(c#) 開發一個貪吃蛇遊戲


玩法
W/S/A/D 或 ↑/↓/←/→ 控制蛇的移動


在線DEMO



思路
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(00, 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
    }
}



OK
[源碼下載]

 

發佈了6 篇原創文章 · 獲贊 1 · 訪問量 2萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章