天天看點

C#貪吃蛇小遊戲

一时兴起做了一个贪吃蛇,借鉴了下面大神的素材,解决了大神没有方向键的问题

 from1

using System;

using System.Collections.Generic;

using System.ComponentModel;

using System.Data;

using System.Drawing;

using System.Linq;

using System.Text;

using System.Threading.Tasks;

using System.Windows.Forms;

namespace 贪吃蛇

{

    public partial class Form1 : Form

    {

        static int score;

        static int foodx, foody;

        KeyEventHandler myKeyEventHandeler;

        KeyboardHook k_hook = new KeyboardHook();

        int lastKey = 0;

        public Form1()

        {

            InitializeComponent();

        }

        private void timer1_Tick(object sender, EventArgs e)

        {

            DrawSnake();

            DrawFood();

        }

        public void DrawFood()

        {

            Graphics f = this.CreateGraphics();

            SolidBrush mybrush = new SolidBrush(Color.Red);

            f.FillRectangle(mybrush, foodx, foody, 10, 10);

        }

        public void DrawSnake()

        {

            RectangleF[] rectangles = new RectangleF[Snake.realsnake.Count];

            int num;

            for (num = 0; num < Snake.realsnake.Count; num++)

            {

                rectangles[num] = new RectangleF(Snake.realsnake[num].Location_x,

                    Snake.realsnake[num].Location_y, Snake.width, Snake.height);

            }

            this.Refresh();

            Graphics g = this.CreateGraphics();

            SolidBrush myBrush = new SolidBrush(Color.Black);

            g.FillRectangles(myBrush, rectangles);

        }

        private void timer2_Tick(object sender, EventArgs e)

        {

            Snake.SnakeMove(Snake.wasd);

            EatFood();

            TobeOrNotTobe();

            label2.Text = score.ToString();

        }

        public void TobeOrNotTobe()

        {

            int x = Snake.realsnake[Snake.realsnake.Count - 1].Location_x;

            int y = Snake.realsnake[Snake.realsnake.Count - 1].Location_y;

            if (!(x <= 500 && x >= 0 && y <= 500 && y >= 0))

            {

                GameOver();

                timer2.Enabled = false;

            }

        }

        public void GameOver()

        {

            label3.Visible = true;

            label4.Visible = true;

            label5.Text = score.ToString();

            label5.Visible = true;

            button1.Text = "重新开始";

            button1.Visible = true;

            stopListen();

        }

        public void EatFood()

        {

            if (Snake.realsnake[Snake.realsnake.Count - 1].Location_x == foodx

               && Snake.realsnake[Snake.realsnake.Count - 1].Location_y == foody)

            {

                newfood();

                Snake.EatLong();

                score += 1;

            }

        }

        public void newfood()

        {

            Food food = new Food();

            foodx = food.Location_x;

            foody = food.Location_y;

        }

        public void button1_Click(object sender, EventArgs e)

        {

            Snake.Snakestart();

            newfood();

            score = 0;

            timer1.Enabled = true;

            timer2.Enabled = true;

            button1.Visible = false;

            label3.Visible = false;

            label4.Visible = false;

            label5.Visible = false;

            startListen();

        }

        private void hook_KeyDown(object sender, KeyEventArgs e)

        {

            //  这里写具体实现

            if (e.KeyCode.Equals(Keys.W)&&lastKey!=2)

            {

                lastKey = 8;

                Snake.wasd = 8;

            }

            if (e.KeyCode.Equals(Keys.S) && lastKey!=8)

            {

                lastKey = 2;

                Snake.wasd = 2;

            }

            if (e.KeyCode.Equals(Keys.A) && lastKey!=6)

            {

                lastKey = 4;

                Snake.wasd = 4;

            }

            if (e.KeyCode.Equals(Keys.D) && lastKey!=4)

            {

                lastKey = 6;

                Snake.wasd = 6;

            }

        }

        /// <summary>

        /// 开始监听

        /// </summary>

        public void startListen()

        {

            

            myKeyEventHandeler = new KeyEventHandler(hook_KeyDown);

            

            k_hook.KeyDownEvent += myKeyEventHandeler;//钩住键按下

            k_hook.Start();//安装键盘钩子

        }

        /// <summary>

        /// 结束监听

        /// </summary>

        public void stopListen()

        {

            if (myKeyEventHandeler != null)

            {

                k_hook.KeyDownEvent -= myKeyEventHandeler;//取消按键事件

                myKeyEventHandeler = null;

                k_hook.Stop();//关闭键盘钩子

            }

        }

    }

}

 

Snake.cs

using System;

using System.Collections.Generic;

using System.Linq;

using System.Text;

using System.Threading.Tasks;

namespace 贪吃蛇

{

    public class Snake

    {

        public int Location_x;//0为x,1为y

        public int Location_y;

        public static int width = 10, height = 10;

        public static List<Snake> realsnake = new List<Snake>();

        //当前方向

        public static int wasd;

        public Snake()

        {

            Location_x = 250;

            Location_y = 250;

        }

        public static void Snakestart()

        {

            wasd = 6;

            realsnake.Clear();

            Snake s0 = new Snake();

            Snake s1 = new Snake();

            Snake s2 = new Snake();

            Snake s3 = new Snake();

            realsnake.Add(s0);

            s1.Location_x = s0.Location_x - 10;

            realsnake.Add(s1);

            s2.Location_x = s1.Location_x - 10;

            realsnake.Add(s2);

            s3.Location_x = s2.Location_x - 10;

            realsnake.Add(s3);

            //添加四个长度,横向添加list 初始化小蛇蛇完成

        }

        public static void SnakeMove(int x)//8426

        {

            int i = realsnake.Count - 1;

            if (x == 8)

            {

                //对list蛇身进行处理

                Snakelist();

                realsnake[i].Location_y -= 10;

            }

            else if (x == 4)

            {

                Snakelist();

                realsnake[i].Location_x -= 10;

            }

            else if (x == 2)

            {

                Snakelist();

                realsnake[i].Location_y += 10;

            }

            else if (x == 6)

            {

                Snakelist();

                realsnake[i].Location_x += 10;

            }

        }

        public static void Snakelist()

        {

            int j = 0;

            int x, y;

            int i = realsnake.Count - 1;  

            for (j = 0; j < i; j++)

            {

                x = realsnake[j + 1].Location_x;

                y = realsnake[j + 1].Location_y;

                realsnake[j].Location_y = y;

                realsnake[j].Location_x = x;

            }

        }

        public static void EatLong()

        {

            Snake S_eat = new Snake();

            int xx = realsnake[realsnake.Count - 1].Location_x;

            int yy = realsnake[realsnake.Count - 1].Location_y;

            if (wasd == 8)

            {

                S_eat.Location_y = yy - 10;

                S_eat.Location_x = xx;

                realsnake.Add(S_eat);

            }

            else if (wasd == 4)

            {

                S_eat.Location_x = xx - 10;

                S_eat.Location_y = yy;

                realsnake.Add(S_eat);

            }

            else if (wasd == 2)

            {

                S_eat.Location_y = yy + 10;

                S_eat.Location_x = xx;

                realsnake.Add(S_eat);

            }

            else if (wasd == 6)

            {

                S_eat.Location_y = yy;

                S_eat.Location_x = xx + 10;

                realsnake.Add(S_eat);

            }

        }

    }

}

 

Food.cs

using System;

using System.Collections.Generic;

using System.Linq;

using System.Text;

using System.Threading.Tasks;

namespace 贪吃蛇

{

   public class Food

    {

        public int Location_x;

        public int Location_y;

        public Food()

        {

            Random x = new Random();

            Random y = new Random();

            Location_x = x.Next(0, 49) * 10;

            Location_y = y.Next(0, 49) * 10;

        }

    }

}

 

KeyboardHook

using System;

using System.Collections.Generic;

using System.Linq;

using System.Runtime.InteropServices;

using System.Text;

using System.Threading.Tasks;

using System.Windows.Forms;

namespace 贪吃蛇

{

    class KeyboardHook

    {

        public event KeyEventHandler KeyDownEvent;

        public event KeyPressEventHandler KeyPressEvent;

        public event KeyEventHandler KeyUpEvent;

        public delegate int HookProc(int nCode, Int32 wParam, IntPtr lParam);

        static int hKeyboardHook = 0; //声明键盘钩子处理的初始值

                                      //值在Microsoft SDK的Winuser.h里查询

        public const int WH_KEYBOARD_LL = 13;   //线程键盘钩子监听鼠标消息设为2,全局键盘监听鼠标消息设为13

        HookProc KeyboardHookProcedure; //声明KeyboardHookProcedure作为HookProc类型

                                        //键盘结构

        [StructLayout(LayoutKind.Sequential)]

        public class KeyboardHookStruct

        {

            public int vkCode;  //定一个虚拟键码。该代码必须有一个价值的范围1至254

            public int scanCode; // 指定的硬件扫描码的关键

            public int flags;  // 键标志

            public int time; // 指定的时间戳记的这个讯息

            public int dwExtraInfo; // 指定额外信息相关的信息

        }

        //使用此功能,安装了一个钩子

        [DllImport("user32.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.StdCall)]

        public static extern int SetWindowsHookEx(int idHook, HookProc lpfn, IntPtr hInstance, int threadId);

        //调用此函数卸载钩子

        [DllImport("user32.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.StdCall)]

        public static extern bool UnhookWindowsHookEx(int idHook);

        //使用此功能,通过信息钩子继续下一个钩子

        [DllImport("user32.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.StdCall)]

        public static extern int CallNextHookEx(int idHook, int nCode, Int32 wParam, IntPtr lParam);

        // 取得当前线程编号(线程钩子需要用到)

        [DllImport("kernel32.dll")]

        static extern int GetCurrentThreadId();

        //使用WINDOWS API函数代替获取当前实例的函数,防止钩子失效

        [DllImport("kernel32.dll")]

        public static extern IntPtr GetModuleHandle(string name);

        public void Start()

        {

            // 安装键盘钩子

            if (hKeyboardHook == 0)

            {

                KeyboardHookProcedure = new HookProc(KeyboardHookProc);

                hKeyboardHook = SetWindowsHookEx(WH_KEYBOARD_LL, KeyboardHookProcedure, GetModuleHandle(System.Diagnostics.Process.GetCurrentProcess().MainModule.ModuleName), 0);

                //hKeyboardHook = SetWindowsHookEx(WH_KEYBOARD_LL, KeyboardHookProcedure, Marshal.GetHINSTANCE(Assembly.GetExecutingAssembly().GetModules()[0]), 0);

                //************************************

                //键盘线程钩子

                SetWindowsHookEx(13, KeyboardHookProcedure, IntPtr.Zero, GetCurrentThreadId());//指定要监听的线程idGetCurrentThreadId(),

                                                                                               //键盘全局钩子,需要引用空间(using System.Reflection;)

                                                                                               //SetWindowsHookEx( 13,MouseHookProcedure,Marshal.GetHINSTANCE(Assembly.GetExecutingAssembly().GetModules()[0]),0);

                                                                                               //

                                                                                               //关于SetWindowsHookEx (int idHook, HookProc lpfn, IntPtr hInstance, int threadId)函数将钩子加入到钩子链表中,说明一下四个参数:

                                                                                               //idHook 钩子类型,即确定钩子监听何种消息,上面的代码中设为2,即监听键盘消息并且是线程钩子,如果是全局钩子监听键盘消息应设为13,

                                                                                               //线程钩子监听鼠标消息设为7,全局钩子监听鼠标消息设为14。lpfn 钩子子程的地址指针。如果dwThreadId参数为0 或是一个由别的进程创建的

                                                                                               //线程的标识,lpfn必须指向DLL中的钩子子程。 除此以外,lpfn可以指向当前进程的一段钩子子程代码。钩子函数的入口地址,当钩子钩到任何

                                                                                               //消息后便调用这个函数。hInstance应用程序实例的句柄。标识包含lpfn所指的子程的DLL。如果threadId 标识当前进程创建的一个线程,而且子

                                                                                               //程代码位于当前进程,hInstance必须为NULL。可以很简单的设定其为本应用程序的实例句柄。threaded 与安装的钩子子程相关联的线程的标识符

                                                                                               //如果为0,钩子子程与所有的线程关联,即为全局钩子

                                                                                               //************************************

                                                                                               //如果SetWindowsHookEx失败

                if (hKeyboardHook == 0)

                {

                    Stop();

                    throw new Exception("安装键盘钩子失败");

                }

            }

        }

        public void Stop()

        {

            bool retKeyboard = true;

            if (hKeyboardHook != 0)

            {

                retKeyboard = UnhookWindowsHookEx(hKeyboardHook);

                hKeyboardHook = 0;

            }

            if (!(retKeyboard)) throw new Exception("卸载钩子失败!");

        }

        //ToAscii职能的转换指定的虚拟键码和键盘状态的相应字符或字符

        [DllImport("user32")]

        public static extern int ToAscii(int uVirtKey, //[in] 指定虚拟关键代码进行翻译。

                                         int uScanCode, // [in] 指定的硬件扫描码的关键须翻译成英文。高阶位的这个值设定的关键,如果是(不压)

                                         byte[] lpbKeyState, // [in] 指针,以256字节数组,包含当前键盘的状态。每个元素(字节)的数组包含状态的一个关键。如果高阶位的字节是一套,关键是下跌(按下)。在低比特,如果设置表明,关键是对切换。在此功能,只有肘位的CAPS LOCK键是相关的。在切换状态的NUM个锁和滚动锁定键被忽略。

                                         byte[] lpwTransKey, // [out] 指针的缓冲区收到翻译字符或字符。

                                         int fuState); // [in] Specifies whether a menu is active. This parameter must be 1 if a menu is active, or 0 otherwise.

        //获取按键的状态

        [DllImport("user32")]

        public static extern int GetKeyboardState(byte[] pbKeyState);

        [DllImport("user32.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.StdCall)]

        private static extern short GetKeyState(int vKey);

        private const int WM_KEYDOWN = 0x100;//KEYDOWN

        private const int WM_KEYUP = 0x101;//KEYUP

        private const int WM_SYSKEYDOWN = 0x104;//SYSKEYDOWN

        private const int WM_SYSKEYUP = 0x105;//SYSKEYUP

        private int KeyboardHookProc(int nCode, Int32 wParam, IntPtr lParam)

        {

            // 侦听键盘事件

            if ((nCode >= 0) && (KeyDownEvent != null || KeyUpEvent != null || KeyPressEvent != null))

            {

                KeyboardHookStruct MyKeyboardHookStruct = (KeyboardHookStruct)Marshal.PtrToStructure(lParam, typeof(KeyboardHookStruct));

                // raise KeyDown

                if (KeyDownEvent != null && (wParam == WM_KEYDOWN || wParam == WM_SYSKEYDOWN))

                {

                    Keys keyData = (Keys)MyKeyboardHookStruct.vkCode;

                    KeyEventArgs e = new KeyEventArgs(keyData);

                    KeyDownEvent(this, e);

                }

                //键盘按下

                if (KeyPressEvent != null && wParam == WM_KEYDOWN)

                {

                    byte[] keyState = new byte[256];

                    GetKeyboardState(keyState);

                    byte[] inBuffer = new byte[2];

                    if (ToAscii(MyKeyboardHookStruct.vkCode, MyKeyboardHookStruct.scanCode, keyState, inBuffer, MyKeyboardHookStruct.flags) == 1)

                    {

                        KeyPressEventArgs e = new KeyPressEventArgs((char)inBuffer[0]);

                        KeyPressEvent(this, e);

                    }

                }

                // 键盘抬起

                if (KeyUpEvent != null && (wParam == WM_KEYUP || wParam == WM_SYSKEYUP))

                {

                    Keys keyData = (Keys)MyKeyboardHookStruct.vkCode;

                    KeyEventArgs e = new KeyEventArgs(keyData);

                    KeyUpEvent(this, e);

                }

            }

            //如果返回1,则结束消息,这个消息到此为止,不再传递。

            //如果返回0或调用CallNextHookEx函数则消息出了这个钩子继续往下传递,也就是传给消息真正的接受者

            return CallNextHookEx(hKeyboardHook, nCode, wParam, lParam);

        }

        ~KeyboardHook()

        {

            Stop();

        }

    }

}

 

最后还是有部分问题未解决,比如吃到自己的尾巴不会死

已经解决了方向键问题

还有希望大家多动动脑子自己进行学习。

繼續閱讀