/*
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
{
/// <summary>
/// A simple sprite element.
/// </summary>
public class Sprite
{
/// <summary>
/// Top left corner of the sprite.
/// </summary>
public Vector2 pos;
/// <summary>
/// Size of the sprite. X is the width, Y is the height.
/// </summary>
public Vector2 size;
/// <summary>
/// Rectangle to copy from the sprite sheet when drawing.
/// </summary>
public Rectangle srcRect;
/// <summary>
/// Texture atlas containing all of the sprite graphics.
/// </summary>
public static Texture2D Sheet = null;
/// <summary>
/// Creates a new sprite.
/// </summary>
/// <param name="srcRect">Rectangle to copy from the sprite sheet when drawing.</param>
/// <param name="pos">Top left corner of the sprite.</param>
/// <param name="size">Size of the sprite. X is the width, Y is the height.</param>
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);
}
/// <summary>
/// Draws the sprite using the given SpriteBatch.
/// </summary>
/// <param name="spriteBatch">SpriteBatch to draw with.</param>
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
/// <summary>
/// The sprite instance associated with the game object.
/// </summary>
public Sprite sprite;
// the sprite position and the game position are seperate, even if they're usually the same
/// <summary>
/// The position of the game object.
/// </summary>
public Vector2 pos;
/// <summary>
/// Updates a game object's logic and sprite. Override in child classes.
/// </summary>
/// <param name="gameTime"></param>
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;
/// <summary>
/// The player sprite.
/// </summary>
Player player;
/// <summary>
/// The maid sprites.
/// </summary>
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<GameObject> selectedObjects = new List<GameObject>();
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<Texture2D>("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);
}
}
}
Comments