@ChristopherBotha wrote:
Scrabbled together from a bunch of bits of code from the web and actually written to test the keyboard hook written by @dale.
U-D-L-R to control, ENTER for new game, SHIFT+X to exit.
Download Snake.rhp (13 KB)
Code: (in case anyone else has an hour to kill
using Rhino; using Rhino.Commands; using Rhino.Display; using Rhino.Geometry; using System; using System.Collections; using System.Collections.Generic; using System.Diagnostics; using System.Drawing; using System.Runtime.InteropServices; using System.Windows.Forms; namespace Snake { [System.Runtime.InteropServices.Guid("d6722cf1-6eea-4d74-b412-14f958452dff")] public class SnakeCommand : Command { public SnakeCommand() { Instance = this; } public static SnakeCommand Instance { get; private set; } public override string EnglishName { get { return "SnakeCommand"; } } private readonly KbListener KeyPress = new KbListener(); public static List<SnakePosition> Snake = new List<SnakePosition>(); public static SnakePosition food = new SnakePosition(); public static SnakeDisplay sn = new SnakeDisplay(); public static int PlayFieldWidth = 50; public static int PlayFieldHeight = 50; public static Timer gameTimer = new Timer(); protected override Result RunCommand(RhinoDoc doc, RunMode mode) { new GameSettings(); gameTimer.Interval = 250 / GameSettings.Speed; gameTimer.Tick += UpdateScreen; gameTimer.Start(); sn.Enabled = true; StartGame(); KeyPress.Enabled = true; return Result.Success; } private void StartGame() { new GameSettings(); Snake.Clear(); SnakePosition head = new SnakePosition { X = 10, Y = 10 }; Snake.Add(head); RhinoApp.WriteLine(GameSettings.Score.ToString()); GenerateFood(); } private void GenerateFood() { int maxXPos = PlayFieldWidth / GameSettings.Width; int maxYPos = PlayFieldHeight / GameSettings.Height; Random random = new Random(); food = new SnakePosition { X = random.Next(0, maxXPos), Y = random.Next(0, maxYPos) }; } private void UpdateScreen(object sender, EventArgs e) { gameTimer.Interval = 250 / GameSettings.Speed; //Check for Game Over if (GameSettings.GameOver) { //Check if Enter is pressed if (Input.KeyPressed(Keys.Enter)) { StartGame(); } if (Input.KeyPressed(Keys.X)) { gameTimer.Stop(); gameTimer.Dispose(); sn.Enabled = false; KeyPress.Enabled = false; } } else { if (Input.KeyPressed(Keys.Right) && GameSettings.direction != Direction.Left) { GameSettings.direction = Direction.Right; } else if (Input.KeyPressed(Keys.Left) && GameSettings.direction != Direction.Right) { GameSettings.direction = Direction.Left; } else if (Input.KeyPressed(Keys.Up) && GameSettings.direction != Direction.Down) { GameSettings.direction = Direction.Up; } else if (Input.KeyPressed(Keys.Down) && GameSettings.direction != Direction.Up) { GameSettings.direction = Direction.Down; } MovePlayer(); } RhinoDoc.ActiveDoc.Views.Redraw(); } private void MovePlayer() { for (int i = Snake.Count - 1; i >= 0; i--) { //Move head if (i == 0) { switch (GameSettings.direction) { case Direction.Right: Snake[i].X++; break; case Direction.Left: Snake[i].X--; break; case Direction.Up: Snake[i].Y++; break; case Direction.Down: Snake[i].Y--; break; } //Get maximum X and Y Pos int maxXPos = PlayFieldWidth / GameSettings.Width; int maxYPos = PlayFieldHeight / GameSettings.Height; //Detect collission with game borders. if (Snake[i].X < 0 || Snake[i].Y < 0 || Snake[i].X >= maxXPos || Snake[i].Y >= maxYPos) { Die(); } //Detect collission with body for (int j = 1; j < Snake.Count; j++) { if (Snake[i].X == Snake[j].X && Snake[i].Y == Snake[j].Y) { Die(); } } //Detect collision with food piece if (Snake[0].X == food.X && Snake[0].Y == food.Y) { Eat(); } } else { //Move body Snake[i].X = Snake[i - 1].X; Snake[i].Y = Snake[i - 1].Y; } } } private void Eat() { SnakePosition circle = new SnakePosition { X = Snake[Snake.Count - 1].X, Y = Snake[Snake.Count - 1].Y }; Snake.Add(circle); GameSettings.Score += GameSettings.Points; GenerateFood(); } private void Die() { GameSettings.GameOver = true; } } internal class Input { private static Hashtable keyTable = new Hashtable(); public static bool KeyPressed(Keys key) { if (keyTable[key] == null) { return false; } return (bool)keyTable[key]; } public static void ChangeState(Keys key, bool state) { keyTable[key] = state; } } public enum Direction { Up, Down, Left, Right }; public class GameSettings { public static int Width { get; set; } public static int Height { get; set; } public static int Speed { get; set; } public static int Score { get; set; } public static int Points { get; set; } public static bool GameOver { get; set; } public static Direction direction { get; set; } public GameSettings() { Width = 2; Height = 2; Speed = 2; Score = 0; Points = 1; GameOver = false; direction = Direction.Down; } } public class SnakePosition { public int X { get; set; } public int Y { get; set; } public SnakePosition(){X = 0;Y = 0;} } public class SnakeDisplay : DisplayConduit { private List<Brep> SnakeBody = new List<Brep>(); protected override void CalculateBoundingBox(CalculateBoundingBoxEventArgs e) { base.CalculateBoundingBox(e); e.IncludeBoundingBox(new BoundingBox( new Point3d((GameSettings.Width / 2) * -1, (GameSettings.Width / 2) * -1, -10), new Point3d(SnakeCommand.PlayFieldWidth, SnakeCommand.PlayFieldHeight, 10))); } protected override void DrawForeground(DrawEventArgs e) { if (SnakeCommand.Snake.Count != 0) { e.Display.DrawBox(new BoundingBox( new Point3d((GameSettings.Width / 2) * -1, (GameSettings.Width / 2) * -1, 0), new Point3d(SnakeCommand.PlayFieldWidth, SnakeCommand.PlayFieldHeight, 0)), Color.DarkCyan,3); if (!GameSettings.GameOver) { SnakeBody.Clear(); foreach (SnakePosition position in SnakeCommand.Snake) { e.Display.DrawSphere( new Sphere(new Point3d(position.X * GameSettings.Width, position.Y * GameSettings.Height, 0), GameSettings.Width / 2), Color.DarkRed); } e.Display.DrawSphere( new Sphere(new Point3d(SnakeCommand.food.X * GameSettings.Width, SnakeCommand.food.Y * GameSettings.Height, 0), GameSettings.Width / 2), Color.DarkGreen); var Text = new Text3d(GameSettings.Score.ToString(), Plane.WorldXY, 5); e.Display.Draw3dText(Text, Color.Blue, new Point3d(20, 20, 0)); } else { RhinoApp.WriteLine("Game over! Score: " + GameSettings.Score + " Enter to try again or SHIFT+X to exit."); } } } } public abstract class KeyboardCallback { private const int WH_KEYBOARD_LL = 13; private const int WM_KEYDOWN = 0x0100; private const int WM_KEYUP = 0x101; private readonly LowLevelKeyboardProc _proc; private static IntPtr _hookID = IntPtr.Zero; protected KeyboardCallback() { _proc = HookCallback; } private bool _enabled; public bool Enabled { get { return _enabled; } set { _enabled = value; if (_enabled) { _hookID = SetHook(_proc); } else { bool unhooked = UnhookWindowsHookEx(_hookID); if (!unhooked) { Trace.TraceError("Unable to unhook low-level keyboard hook"); } } } } private IntPtr SetHook(LowLevelKeyboardProc proc) { using (Process curProcess = Process.GetCurrentProcess()) using (ProcessModule curModule = curProcess.MainModule) { return SetWindowsHookEx(WH_KEYBOARD_LL, proc, GetModuleHandle(curModule.ModuleName), 0); } } private delegate IntPtr LowLevelKeyboardProc(int nCode, IntPtr wParam, IntPtr lParam); private IntPtr HookCallback(int nCode, IntPtr wParam, IntPtr lParam) { if (nCode >= 0) { IntPtr foregroundWindow = GetForegroundWindow(); IntPtr undefined = (IntPtr)(-1); if (foregroundWindow == undefined) { return CallNextHookEx(IntPtr.Zero, nCode, wParam, lParam); } uint processID; GetWindowThreadProcessId(foregroundWindow, out processID); uint currentProcessID = GetCurrentProcessId(); if (processID != currentProcessID) // foreground window is not my process { return CallNextHookEx(IntPtr.Zero, nCode, wParam, lParam); } if (wParam == (IntPtr)WM_KEYDOWN) { int vkCode = Marshal.ReadInt32(lParam); bool handled = false; OnKeyDown((Keys)vkCode, ref handled); if (handled) { return (IntPtr)1; } } else if (wParam == (IntPtr)WM_KEYUP) { int vkCode = Marshal.ReadInt32(lParam); bool handled = false; OnKeyUp((Keys)vkCode, ref handled); if (handled) { return (IntPtr)1; } } } return CallNextHookEx(_hookID, nCode, wParam, lParam); } public abstract void OnKeyDown(Keys key, ref bool handled); public abstract void OnKeyUp(Keys key, ref bool handled); [DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)] private static extern IntPtr SetWindowsHookEx(int idHook, LowLevelKeyboardProc lpfn, IntPtr hMod, uint dwThreadId); [DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)] [return: MarshalAs(UnmanagedType.Bool)] private static extern bool UnhookWindowsHookEx(IntPtr hhk); [DllImport("user32.dll")] private static extern IntPtr GetForegroundWindow(); [DllImport("user32.dll", SetLastError = true)] private static extern uint GetWindowThreadProcessId(IntPtr hWnd, out uint lpdwProcessId); [DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)] private static extern IntPtr CallNextHookEx(IntPtr hhk, int nCode, IntPtr wParam, IntPtr lParam); [DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)] private static extern IntPtr GetModuleHandle(string lpModuleName); [DllImport("kernel32.dll")] private static extern uint GetCurrentProcessId(); } public class KbListener : KeyboardCallback { public override void OnKeyDown(Keys key, ref bool handled) { Input.ChangeState(key, true); handled = true; } public override void OnKeyUp(Keys key, ref bool handled) { Input.ChangeState(key, false); handled = true; } } }
Posts: 1
Participants: 1