/* You can move the player sprite with WASD and use the mouse to click on the maids to select them. Ctrl-clicking selects multiple maids (it acts as a toggle, so you can deselect the same way). Clicking the ground (nothing) clears what you had selected. Right clicking will send selected maids to that position. Game1.cs needs major overhauls and a metric ton of commenting still. */ // ================================ /2d api/Sprite.cs ================================ using System; using Microsoft.Xna.Framework; using Microsoft.Xna.Framework.Graphics; namespace ano_sekai { /// /// A simple sprite element. /// public class Sprite { /// /// Top left corner of the sprite. /// public Vector2 pos; /// /// Size of the sprite. X is the width, Y is the height. /// public Vector2 size; /// /// Rectangle to copy from the sprite sheet when drawing. /// public Rectangle srcRect; /// /// Texture atlas containing all of the sprite graphics. /// public static Texture2D Sheet = null; /// /// Creates a new sprite. /// /// Rectangle to copy from the sprite sheet when drawing. /// Top left corner of the sprite. /// Size of the sprite. X is the width, Y is the height. public Sprite(Rectangle srcRect, Vector2 pos, Vector2 size) { this.srcRect = srcRect; this.pos = pos; this.size = size; } public void Update(GameTime gameTime) { // nothing to update yet, but when animations are put in, we'll need this } public Rectangle GetDestRect() { return new Rectangle((int)pos.X, (int)pos.Y, (int)size.X, (int)size.Y); } /// /// Draws the sprite using the given SpriteBatch. /// /// SpriteBatch to draw with. public void Draw(SpriteBatch spriteBatch) { // TODO: throw exception if Sprite.sheet is null // using destination rectangle in SpriteBatch.Draw to keep things simple spriteBatch.Draw(Sheet, GetDestRect(), srcRect, Color.White); } } } // ================================ /engine/GameObject.cs ================================ using System; using Microsoft.Xna.Framework; namespace ano_sekai { public class GameObject { // don't derive game object classes from sprites // -> once Sprite gets alot of public functions and variables, it'll clutter // intellisense for game objects too /// /// The sprite instance associated with the game object. /// public Sprite sprite; // the sprite position and the game position are seperate, even if they're usually the same /// /// The position of the game object. /// public Vector2 pos; /// /// Updates a game object's logic and sprite. Override in child classes. /// /// public virtual void Update(GameTime gameTime) { sprite.Update(gameTime); } } } // ================================ /engine/Input.cs ================================ using System; using Microsoft.Xna.Framework; using Microsoft.Xna.Framework.Content; using Microsoft.Xna.Framework.Graphics; using Microsoft.Xna.Framework.Input; namespace ano_sekai { public class Input { public static KeyboardState keyboard, lastKeyboard; public static MouseState mouse, lastMouse; public static GamePadState gamepad, lastGamepad; public static void Update() { lastKeyboard = keyboard; keyboard = Keyboard.GetState(); lastMouse = mouse; mouse = Mouse.GetState(); lastGamepad = gamepad; gamepad = GamePad.GetState(PlayerIndex.One); } } } // ================================ /Game1.cs ================================ using System; using System.Collections.Generic; using Microsoft.Xna.Framework; using Microsoft.Xna.Framework.Content; using Microsoft.Xna.Framework.Graphics; using Microsoft.Xna.Framework.Input; namespace ano_sekai { public class Maid : GameObject { public Vector2 destPos; public Maid(Vector2 startPos) { pos = startPos; destPos = pos; // keeps it in place sprite = new Sprite( new Rectangle(0, 16, 16, 16), // source rectangle startPos, // top left new Vector2(16, 16) // size ); } public override void Update(GameTime gameTime) { if (pos != destPos) { float dist = (destPos - pos).Length(); Vector2 dir = (destPos - pos) / dist; Vector2 moveVec = dir * 32 * (float)gameTime.ElapsedGameTime.TotalSeconds; if (moveVec.Length() >= dist) pos = destPos; else pos += moveVec; } // why do i have to do this every time? sprite.pos = pos; base.Update(gameTime); } } public class Game1 : Game { GraphicsDeviceManager graphics; SpriteBatch spriteBatch; /// /// The player sprite. /// Player player; /// /// The maid sprites. /// Maid[] maids; // left-click object to select, left-click nothing to deselect // right-click will perform actions (maybe hold for a menu?) // TODO: multiple selection List selectedObjects = new List(); public Game1() { // some stuff added by default, just leave it here graphics = new GraphicsDeviceManager(this); Content.RootDirectory = "Content"; // show the mouse over the window IsMouseVisible = true; // set the window title Window.Title = "Ano Sekai"; // set the size of the window's client area graphics.PreferredBackBufferWidth = 800; graphics.PreferredBackBufferHeight = 600; } protected override void LoadContent() { // load the sprite sheet from pipeline // the .png is located at e:/coding/projects/games/ano sekai/current/ Sprite.Sheet = Content.Load("sprite sheet"); // create the sprite batch so it's ready when we need it spriteBatch = new SpriteBatch(GraphicsDevice); // create a temporary player sprite so we can fiddle with stuff player = new Player(new Vector2(32, 32)); // create temporary maid sprites. // TODO: will soon be selectable and order-giveable maids = new Maid[3]; maids[0] = new Maid(new Vector2(64, 96)); maids[1] = new Maid(new Vector2(96, 64)); maids[2] = new Maid(new Vector2(96, 96)); } protected override void Update(GameTime gameTime) { Input.Update(); // press esc to exit if (Input.keyboard.IsKeyDown(Keys.Escape)) Exit(); // "command" inputs done seperately from "hero" input if (Input.mouse.LeftButton == ButtonState.Pressed && Input.lastMouse.LeftButton == ButtonState.Released) { bool clickedTheGround = true; for (int m = 0; m < maids.Length; m++) { // gotta divide by 4 cuz we're scaling shit up // TODO: deal with this better if (maids[m].sprite.GetDestRect().Contains(Input.mouse.X / 4, Input.mouse.Y / 4)) { clickedTheGround = false; // control will add or remove them if (!Input.keyboard.IsKeyDown(Keys.LeftControl)) selectedObjects.Clear(); if (selectedObjects.Contains(maids[m])) selectedObjects.Remove(maids[m]); else selectedObjects.Add(maids[m]); } } if (clickedTheGround) selectedObjects.Clear(); } if (Input.mouse.RightButton == ButtonState.Pressed && Input.lastMouse.RightButton == ButtonState.Released) { // we'd check to see if any enemy units were being selected first, but none yet so just move // dividing by 4 because we're scaling again // TODO: deal with this smarter Vector2 dest = new Vector2(Input.mouse.X / 4, Input.mouse.Y / 4); foreach (GameObject obj in selectedObjects) { if (obj.GetType() == typeof(Maid)) ((Maid)obj).destPos = dest; } } player.Update(gameTime); for (int m = 0; m < maids.Length; m++) maids[m].Update(gameTime); base.Update(gameTime); } protected override void Draw(GameTime gameTime) { // clear the screen GraphicsDevice.Clear(Color.CornflowerBlue); // scale sprites by 4x Matrix drawMatrix = Matrix.CreateScale(4.0f); // start drawing sprites. using PointClamp to give us sharp pixel edges for that retro look. spriteBatch.Begin(SpriteSortMode.Deferred, null, SamplerState.PointClamp, null, null, null, drawMatrix); { // draw the maid sprites for (int m = 0; m < maids.Length; m++) { if (selectedObjects.Contains(maids[m])) spriteBatch.Draw(Sprite.Sheet, maids[m].sprite.GetDestRect(), new Rectangle(16, 16, 16, 16), Color.Yellow); maids[m].sprite.Draw(spriteBatch); } // draw the player sprite player.sprite.Draw(spriteBatch); } // done drawing sprites spriteBatch.End(); base.Draw(gameTime); } } }