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;
            }
        }
    }
    

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

  • 相关阅读:
    Netty Java原生和Netty网络的API比较
    MYSQL登录函数(第3版本)
    利用MYSQL的函数实现用户登录功能,进出都是JSON(第二版)
    UC登录功能:商户需要创建的表
    利用MYSQL的函数实现用户登录功能,进出都是JSON(第一版)
    MYSQL存储过程实现用户登录
    FreeBsd网络性能优化方案sysctl
    利用飞儿云PHP框架自带的DNSPOD库做DDNS动态域名解析
    Linux根据MAC地址自动设置IP
    在KVM里装个pfSense
  • 原文地址:https://www.cnblogs.com/rbzz/p/8068033.html
Copyright © 2011-2022 走看看