zoukankan      html  css  js  c++  java
  • 简易贪吃蛇

    关于贪吃蛇,一直都想做一个,但一直以来奈何总是没有任何思路,经过这几天的思索,总算是给弄出来了

    主要用到的是 Graphics 

    现在来看这张图,这张图假定每一个单元格是10*10的,那么在这张图里,蛇的每一节就是10*10的大小

    可以看出当前蛇的长度是"3"节,假定这一个单元格是10像素的话,

    这3节蛇身的坐标分别是  0,0    10,0    20,0

    默认情况下蛇应该是向右前进的,那么怎么模拟蛇身在运动呢?

    思路是这样的,首先在蛇头也就是最右边位置(坐标20,0) 的前面(坐标30,0)位置再画一个蓝色的单元格

    就像这样

    然后擦掉最左边的蛇尾(坐标0,0)

    就变成这样了

    现在蛇身是不是向右前进一步了

    其实就是将横坐标每次+10,而纵坐标不变

    然后一直重复这个过程,就变成了蛇一直在向右前进

    -----------------------------------------------------------顽皮的分割线--------------------------------------------------------

    接着是上下左右的问题

    首先来模拟向下

    向下的话,就是在当前的蛇头位置,例如当前是30,0 这个位置的下方再画一个蓝色单元格

    就像这样的

    此时蛇头的坐标就应该是(30,10),  其实就是将新加的蛇头的纵坐标加了10

    然后擦掉尾部的10,0坐标

    然后一直重复这个过程(也就是横坐标不变,纵坐标一直加10),就模拟出蛇身向下运动了

    然后向左的话,就是横坐标-10 而纵坐标不变

    向上的话就是,横坐标不变,纵坐标-10

    基本的思路就是这样,在蛇身运动的过程中不变的增加蛇头擦除蛇尾

    而在更改方向的时候就改变其对应的横纵坐标.

    ------------------------------------------------------------------------------------俏皮的分割线--------------------------------------------------------------

    接着说下食物的投放

    食物的话,采取随机投放

    但投放的区域是有讲究的

    例如项目的地图是一个720*720的方框,蛇身一节是10*10的话,那么食物的大小也应该是10*10

    而食物的投放区域也不应该超出地图,不然蛇根本吃不到怎么搞是吧

    我一刚开始想的是创建随机器,然后随机在0-720以内进行取值

    可是,因为你是10*10的大小,如果随机出来了的横坐标是3,纵坐标是5的话

    那么这个食物就会出现不在蛇身的运动轨迹上

    所以对于食物的投资区域我是( 0-720/食物宽度)*食物宽度,这样出来的坐标总是能被他的宽度整除

    例如如果随机出来的两个坐标数字是3和5

    那么经过以上处理出来的坐标就应该是30,50,蛇是肯定能完全吃到的

    --------------------------------------------------没用的分割线---------------------------------------------------------------------------

    好了,现在开始制作代码方面的事

    新建一个窗体程序后,我是打算直接用一个图片框当地图的,图片框的大小设置为720*720

    而蛇身和食物的大小都设置为10*10

    界面搭建

    = =| 额,不会搭界面,就这样吧

    然后,直接贴全部的代码

     ===================元素分割线================================

    (之前发过代码,但是因为感觉尚有不足,以及臃肿之处,所以一直编辑来编辑去的修改,这次又改了,所以决定将之前的代码完全删掉再重新贴一次)

    (有不足之处,请看官们多多包涵)

     1     /// <summary>
     2     /// 蛇身或食物的基本块(父类)
     3     /// </summary>
     4     public class Cell
     5     {
     6         /// <summary>
     7         /// 块的顶点X座标
     8         /// </summary>
     9         public int X { get; set; }
    10 
    11         /// <summary>
    12         /// 块的顶点Y座标
    13         /// </summary>
    14         public int Y { get; set; }
    15 
    16         /// <summary>
    17         /// 块的宽度
    18         /// </summary>
    19         public int Width { get; set; }
    20 
    21         /// <summary>
    22         /// 块的高度
    23         /// </summary>
    24         public int Height { get; set; }
    25 
    26         /// <summary>
    27         /// 所在地图宽度
    28         /// </summary>
    29         public int MapWidth { get; set; }
    30 
    31         /// <summary>
    32         /// 所在地图高度
    33         /// </summary>
    34         public int MapHeight { get; set; }
    35 
    36         public Cell(int x,int y)
    37         {
    38             this.X = x;
    39             this.Y = y;
    40         }
    41 
    42         public Cell() { }
    43 
    44         /// <summary>
    45         /// 绘制单元格
    46         /// </summary>
    47         /// <param name="g"></param>
    48         /// <param name="c"></param>
    49         public virtual void Draw(Graphics g, Color c)
    50         {
    51             SolidBrush sb = new SolidBrush(c);
    52             g.FillRectangle(sb, this.X, this.Y, this.Width, this.Height);
    53         }
    54     }
     1     /// <summary>
     2     /// 食物类
     3     /// </summary>
     4     public class Food:Cell
     5     {
     6         public Food(int x, int y) : base(x, y)
     7         {
     8 
     9         }
    10 
    11         public Food() { }
    12 
    13         /// <summary>
    14         /// 创建食物
    15         /// </summary>
    16         /// <returns></returns>
    17         public Food GetFood()
    18         {
    19 
    20             Random r = new Random();
    21 
    22             ///-------------------------------------------------------
    23             ///--因为本例中所使用的地图是正方形,每一个食物都是在地图的小正方形中
    24             ///其生成的坐标点必须要能被整除,所以下面才这样获取随机数
    25             int rX = (r.Next(0, this.MapWidth / Width)) * Width;
    26             int rY = (r.Next(0, this.MapHeight / Width)) * Width;            
    27 
    28             //生成新的食物,并给各项赋值
    29             Food Newfood = new Food(rX, rY);
    30             Newfood.Width = this.Width;
    31             Newfood.Height = this.Height;
    32             return Newfood;
    33         }           
    34     }
      1     /// <summary>
      2     /// 蛇类
      3     /// </summary>
      4     public class Snake: Cell
      5     {
      6         /// <summary>
      7         /// 创建食物对象
      8         /// </summary>
      9         Food food = new Food();
     10       
     11         /// <summary>
     12         /// 创建蛇身集合
     13         /// </summary>
     14         public List<Snake> Snakes = new List<Snake>();
     15 
     16         /// <summary>
     17         /// 创建头部坐标
     18         /// </summary>
     19         public Point Heade;
     20 
     21         public Snake() { }
     22 
     23         public Snake(int x, int y) : base(x, y)
     24         {
     25         }
     26 
     27         /// <summary>
     28         /// 位置枚举
     29         /// </summary>
     30         public enum Direction { left, right, up, down }
     31              
     32         /// <summary>
     33         /// 创建枚举对象
     34         /// </summary>
     35         public Direction de = new Direction();
     36 
     37         /// <summary>
     38         /// 画出蛇的形状
     39         /// </summary>
     40         /// <param name="g"></param>
     41         /// <param name="c"></param>
     42         public override void Draw(Graphics g, Color c)
     43         {
     44             //创建单色刷子
     45             SolidBrush sb = new SolidBrush(c);
     46 
     47             foreach (var item in Snakes)
     48             {
     49                 //将蛇身每一节画出来 
     50                 g.FillRectangle(sb, item.X, item.Y,this.Width,this.Height);
     51             }
     52         }
     53 
     54         /// <summary>
     55         /// 清除蛇尾的那节
     56         /// </summary>
     57         /// <param name="g"></param>
     58         /// <param name="c"></param>
     59         public void Clear(Graphics g, Color c)
     60         {
     61             //将尾部一个给涂掉
     62             Snake s = Snakes[0];
     63             SolidBrush sb = new SolidBrush(c);
     64             g.FillRectangle(sb, s.X, s.Y, Width, Height);
     65         }
     66 
     67         /// <summary>
     68         /// 移动的方法
     69         /// </summary>
     70         /// <param name="g"></param>
     71         /// <param name="c"></param>
     72         /// <param name="ph">当前的朝向</param>
     73         public void Move(Graphics g,Color c,Direction ph)
     74         {
     75             //根据朝向更改对应的坐标
     76             switch (ph)
     77             {
     78                 case Direction.left:
     79                     Heade.X -= Width;
     80                     break;
     81                 case Direction.right:
     82                     Heade.X += Width;
     83                     break;
     84                 case Direction.up:
     85                     Heade.Y -= Height;
     86                     break;
     87                 case Direction.down:
     88                     Heade.Y += Height;
     89                     break;
     90             }
     91 
     92             //将新蛇头加入到集合中
     93             Snakes.Add(new Snake(Heade.X, Heade.Y));
     94         }
     95 
     96         /// <summary>
     97         /// 判断当前是否吃到食物
     98         /// </summary>
     99         /// <param name="food"></param>
    100         /// <returns></returns>
    101         public bool EatFood(Food food)
    102         {
    103             //拿到食物座标
    104             Point foodPos = new Point(food.X, food.Y);
    105 
    106             //如果当前的蛇头座标与当前的食物座标重叠
    107             if (Heade.X == foodPos.X && Heade.Y == foodPos.Y)
    108             {
    109                 //已经吃到食物了
    110                 return true;
    111             }
    112             else
    113             {
    114                 return false;
    115             }
    116         }
    117 
    118         /// <summary>
    119         /// 判断 是否蛇头撞到了自己
    120         /// </summary>
    121         /// <returns>返回true则为撞到</returns>
    122         public bool HitBOdy()
    123         {
    124             bool falg = false;
    125             
    126             //判断蛇头是否在除蛇头之外的蛇身内
    127             for (int i = 0; i < Snakes.Count-1; i++)
    128             {
    129                 //如果在
    130                 if (Snakes[i].X == Heade.X && Snakes[i].Y == Heade.Y)//Snakes[i].X == snakeHead.X && Snakes[i].Y == snakeHead.Y)
    131                 {
    132                     //撞到自己
    133                     falg = true;
    134                     break;
    135                 }
    136                 else
    137                 {
    138                     falg = false;
    139                 }
    140             }
    141             return falg;
    142         }
    143 
    144         /// <summary>
    145         /// 判断是否跑出了地图
    146         /// </summary>
    147         /// <returns></returns>
    148         public bool HitMap()
    149         {
    150             ///判断当前蛇头位置是否在地图外面
    151             if (Heade.X< 0 || Heade.X >= MapWidth || Heade.Y < 0 || Heade.Y >= MapHeight)
    152             {
    153                 return true;
    154             }
    155             else
    156             {
    157                 return false;
    158             }
    159         }
    160     }
        public partial class 贪吃蛇 : Form
        {
    
            public 贪吃蛇()
            {
                InitializeComponent();
    
            }       
    
            /// <summary>
            /// 初始化蛇
            /// </summary>
            Snake snake = new Snake();
    
            /// <summary>
            /// 创建食物
            /// </summary>
            Food food = null;
    
            /// <summary>
            /// 创建对象,用作地图(也就是大图片框)
            /// </summary>
            Graphics gp;
    
            /// <summary>
            /// 窗体初始化
            /// </summary>
            /// <param name="sender"></param>
            /// <param name="e"></param>
            private void 贪吃蛇_Load(object sender, EventArgs e)
            {
                ///创建画板
                gp = pictureBox1.CreateGraphics();
    
                //初始化蛇身朝向(默认为向右)
                snake.de = Snake.Direction.right;
    
                timer1.Interval = hScrollBar1.Value;
                lbSpeed.Text = hScrollBar1.Value.ToString();
                lbSnakeCount.Text = snake.Snakes.Count.ToString();
    
                //--初始化蛇的各项数值
                snake.Width = 10;
                snake.Height = 10;
                snake.MapHeight = pictureBox1.Height;
                snake.MapWidth = pictureBox1.Width;
    
                //--增加蛇身
                snake.Snakes.Add(new Snake(0, 0));
                snake.Snakes.Add(new Snake(10, 0));
                snake.Snakes.Add(new Snake(20, 0));
                snake.Snakes.Add(new Snake(30, 0));
    
                //--初始化食物的各项数值
                food = new SnakeGame.Food(100, 100);
                food.Width = 10;
                food.Height = 10;
                food.MapWidth = pictureBox1.Width;
                food.MapHeight = pictureBox1.Height;            
            }
    
            private void 贪吃蛇_Paint(object sender, PaintEventArgs e)
            {
                //创建一张位图,大小就为图片框的大小
                Bitmap bitmap = new Bitmap(this.pictureBox1.ClientSize.Width, this.pictureBox1.ClientSize.Height);
                Graphics g = Graphics.FromImage(bitmap);
    
                //画出初始蛇身
                snake.Draw(g, Color.White);
    
                //画出初始食物
                food.Draw(g, Color.Red);
    
                this.pictureBox1.Image = bitmap;
            }
    
            /// <summary>
            ///  开始按钮
            /// </summary>
            /// <param name="sender"></param>
            /// <param name="e"></param>
            private void btnStart_Click(object sender, EventArgs e)
            {
                //点击开始按钮后,启动计时器
                timer1.Enabled = true;
            }
    
    private void timer1_Tick(object sender, EventArgs e) { /// 逻辑为: /// 模拟蛇身在运动 /// 即先让蛇尾那一格涂成和背景色一样,然后判断方向, /// 在相应的方向增加蛇头位置,之后再将新蛇头画出来 //每次刷新的时候都取一次蛇头位置(给蛇头赋值) snake.Heade = new Point(snake.Snakes.Last().X, snake.Snakes.Last().Y); //清除蛇尾 snake.Clear(gp, pictureBox1.BackColor); //判断是否吃到食物,吃到了就不再移除蛇尾,当做是吃了食物增长了一节 if (snake.EatFood(food)) { //已经吃了就再获取一次食物 food = food.GetFood(); //将重新获取到食物对象赋值 food.MapHeight = pictureBox1.Height; food.MapWidth = pictureBox1.Width; //画出重新获取到的食物 food.Draw(gp, Color.Red); //每增长5节数减10速度 timer1.Interval = Level.levelUp(snake.Snakes.Count, 5, timer1.Interval, 10); lbSpeed.Text = timer1.Interval.ToString(); } else { //移除蛇尾 snake.Snakes.RemoveAt(0); }
    //只用直接调用蛇类的行走方法,传入当前的朝向就可以了 snake.Move(gp, Color.Red, snake.de); //画出整个蛇身 snake.Draw(gp, Color.White); //判断边界 if (snake.HitBOdy()) { timer1.Enabled = false; MessageBox.Show("撞到自己了,游戏结束"); //将开始按钮禁用 btnStart.Enabled = false; return; } if (snake.HitMap()) { timer1.Enabled = false; MessageBox.Show("撞到墙了,游戏结束"); //将开始按钮禁用 btnStart.Enabled = false; return; } lbSnakeCount.Text = snake.Snakes.Count.ToString(); } /// <summary> /// 开始按钮点击后,判断WSDA /// </summary> /// <param name="sender"></param> /// <param name="e"></param> private void btnStart_KeyDown(object sender, KeyEventArgs e) { //根据当前的按键将朝向对应起来 switch (e.KeyCode) { case Keys.W: snake.de = Snake.Direction.up; break; case Keys.S: snake.de = Snake.Direction.down; break; case Keys.D: snake.de = Snake.Direction.right; break; case Keys.A: snake.de = Snake.Direction.left; break; } } /// <summary> /// 暂停按钮 /// </summary> /// <param name="sender"></param> /// <param name="e"></param> private void btnPause_Click(object sender, EventArgs e) { //将计时器不可用 timer1.Enabled = false; } /// <summary> /// 拉动速度条时改变蛇的速度 /// </summary> /// <param name="sender"></param> /// <param name="e"></param> private void hScrollBar1_ValueChanged(object sender, EventArgs e) { this.timer1.Interval = hScrollBar1.Value; lbSpeed.Text = hScrollBar1.Value.ToString(); btnStart.Focus(); } /// <summary> /// 重新开始 /// </summary> /// <param name="sender"></param> /// <param name="e"></param> private void btnAgain_Click(object sender, EventArgs e) { //重新开始,初始化各项数值 snake.Snakes.Clear(); snake.de = Snake.Direction.right; this.贪吃蛇_Paint(sender, null); this.贪吃蛇_Load(sender, null); btnStart.Focus(); btnStart.Enabled = true; } /// <summary> /// 画布点击,只是为了增加改变地图的背景色而已,没多大用 /// </summary> /// <param name="sender"></param> /// <param name="e"></param> private void pictureBox1_Click(object sender, EventArgs e) { if (colorDialog1.ShowDialog() == DialogResult.OK) { this.pictureBox1.BackColor = colorDialog1.Color; } } /// <summary> /// 窗体点击,只是为了增加改变窗体的背景色而已,没多大用 /// </summary> /// <param name="sender"></param> /// <param name="e"></param> private void 贪吃蛇_Click(object sender, EventArgs e) { if (colorDialog1.ShowDialog() == DialogResult.OK) { this.BackColor = colorDialog1.Color; } } /// <summary> /// 背景改变,只是为了增加改变美女图片框的背景色而已,让其和窗体背景色保持一致,没多大用 /// </summary> /// <param name="sender"></param> /// <param name="e"></param> private void 贪吃蛇_BackColorChanged(object sender, EventArgs e) { pictureBox2.BackColor = this.BackColor; } }
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.Threading.Tasks;
    
    namespace SnakeGame
    {
        /// <summary>
        /// 等级增长类
        /// </summary>
        public static class Level
        {
            /// <summary>
            /// 等级增长,传入蛇身的长度,每当增长的长度就减少时间间隔(增长蛇的行动速度)
            /// </summary>
            /// <param name="count">蛇身当前长度</param>
            /// <param name="length">当增长到一定的节数</param>
            /// <param name="time">当前计时器间隔时间</param>
            /// <param name="speed">每次减去的速度</param>
            /// <returns></returns>
            public static int levelUp(int count,int length, int time,int speed)
            {
                if (count%length==0&&time>0)
                {
                    time -= speed;
                }
                return time;
            }
        }
    }
    

      最后,本示例所用环境如下图所示

  • 相关阅读:
    PHP 5.5.0 Alpha5 发布
    Ubuntu Touch 只是另一个 Android 皮肤?
    MariaDB 10 已经为动态列提供文档说明
    Percona Toolkit 2.1.9 发布,MySQL 管理工具
    Oracle Linux 6.4 发布
    Ruby 2.0.0 首个稳定版本(p0)发布
    Apache Pig 0.11.0 发布,大规模数据分析
    Node.js 0.8.21 稳定版发布
    红薯 MySQL 5.5 和 5.6 默认参数值的差异
    Django 1.5 正式版发布,支持 Python 3
  • 原文地址:https://www.cnblogs.com/rbzz/p/8068033.html
Copyright © 2011-2022 走看看