First Commit

main
Nathan Steel 1 year ago
commit d49d608e10

@ -0,0 +1,2 @@
# Entities

@ -0,0 +1,14 @@
position = {}
size = {}
team = {
good : {}
,bad : {}
,allied : {}
}
direction = {}
zindex = {}
speed = {}

@ -0,0 +1,3 @@
entities = {};
entityCount = 0;
entityLive = 0; // To keep track of how many active entities there are (just a count)

@ -0,0 +1,9 @@
const GAME_VERSION = 'v0.0';
const ctx = canvas.getContext('2d');
let canvasLeft = canvas.offsetLeft + canvas.clientLeft;
let canvasTop = canvas.offsetTop + canvas.clientTop;
ctx.font = "12px Arial";
canvas.style.backgroundColor = 'rgb(143 153 150)';
ctx.fillStyle = '#000';

@ -0,0 +1,32 @@
function printText(text, positionX, positionY, alignment = 'left', baseline = 'alphabetic', style = 'normal', weight = 'normal', size = '10', font = 'Arial', colour = '#000', strokeStyle = false, lineWidth = false, strokeOnly = false){
// Save the styling, and content already on the canvas
ctx.save();
// Do the alterations and print the text
ctx.textAlign = alignment;
ctx.textBaseline = baseline;
// Set the font styling
ctx.font = style+' '+weight+' '+size+'pt'+' '+font;
//ctx.font-style = fontStyle; // normal, italic, oblique
ctx.fillStyle = colour;
if(strokeStyle && lineWidth){
// Set the stroke styling
ctx.strokeStyle = strokeStyle;
ctx.lineWidth = lineWidth;
// Add the stroke (first, before fill) as it looks better
ctx.strokeText(text, positionX, positionY);
}
if(!strokeOnly){
// Actually add the text
ctx.fillText(text, positionX, positionY);
}
// Restore the prior existing canvas content
ctx.restore();
}

@ -0,0 +1,33 @@
<!DOCTYPE HTML>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Entities</title>
</head>
<body>
<canvas id="canvas" width="1000" height="600"></canvas>
<hr>
<hr id="canvasRule">
</body>
<!-- Scripts -->
<script src="global.js"></script>
<script src="spriteCanvas.js"></script>
<script src="components.js"></script>
<script src="entities.js"></script>
<script src="shapes.js"></script>
<script src="helpers.js"></script>
<script src="interaction.js"></script>
<script src="main.js"></script>
</html>

@ -0,0 +1,187 @@
// Which interaction occurred for logic checks
const INTERACTION = {
'left_click' : 0
, 0 : 'left_click'
,'right_click' : 1
, 1 : 'right_click'
};
// Mouse data
const MOUSE = {
down : false
,x : 0
,y : 0
,button : [0,0,0] // Left, Middle, Right
};
// Keypress data
const KEYPRESS = {
w : 0
,a : 0
,s : 0
,d : 0
,shift : 0
}
// MOUSE
// https://stackoverflow.com/a/29957988
canvas.addEventListener('mousedown', function(event) {
MOUSE.button[event.button] = 1; // Button down bool
MOUSE.down = true;
});
canvas.addEventListener('mouseup', function(event) {
MOUSE.button[event.button] = 0; // Button not down bool
MOUSE.down = false;
});
canvas.addEventListener('mouseout', function(event) {
MOUSE.down = false;
});
canvas.addEventListener('mousemove', function(event) {
MOUSE.x = event.pageX - canvasLeft;
MOUSE.y = event.pageY - canvasTop;
});
// Left click
canvas.addEventListener('click', function(event) {
entityInteractionCheck(
event.pageX - canvasLeft,
event.pageY - canvasTop,
INTERACTION.left_click
);
});
// Right click
canvas.addEventListener('contextmenu', function(event) {
event.preventDefault();
entityInteractionCheck(
event.pageX - canvasLeft,
event.pageY - canvasTop,
INTERACTION.right_click
);
});
// Check if something has been clicked (via position)
// Allows to pass an entity OR an area to check
function clickableCheck(cursorX,cursorY,entity = null, area = null){
if(entity == null && area == null){ return false; }
if(entity != null){
// Set area to size/pos of entity
area = {
'x': position[entity].x,
'y': position[entity].y,
'width': size[entity].width,
'height': size[entity].height
}
}
// Collision detection between clicked offset and clickableItems
// https://stackoverflow.com/a/9880302
if(
cursorY > area['y'] && cursorY < area['y'] + area['height']
&& cursorX > area['x'] && cursorX < area['x'] + area['width']
)
{
return true;
}
return false;
}
// KEYBOARD
window.addEventListener("keydown", onKeyDown, false);
window.addEventListener("keyup", onKeyUp, false);
function onKeyDown(event) {
if(event.shiftKey){ KEYPRESS.shift = true; }
var keyCode = event.keyCode;
switch (keyCode) {
case 87:
KEYPRESS.w = true;
break;
case 65:
KEYPRESS.a = true;
break;
case 83:
KEYPRESS.s = true;
break;
case 68:
KEYPRESS.d = true;
break;
}
}
function onKeyUp(event) {
if(event.shiftKey){ KEYPRESS.shift = false; }
var keyCode = event.keyCode;
switch (keyCode) {
case 87:
KEYPRESS.w = false;
break;
case 65:
KEYPRESS.a = false;
break;
case 83:
KEYPRESS.s = false;
break;
case 68:
KEYPRESS.d = false;
break;
}
}
// HELPERS (Should be for all)
function entityInteractionCheck(x,y,interaction){
// Loop the entities that are interactable
// I.e. Have size, and positions
console.log('Interaction: '+INTERACTION[interaction]);
console.log('Positions; X: '+x+' Y: '+y);
for (const [entity] of Object.entries(size)) {
// If there's no position (or size), skip
if(position[entity] === undefined){ continue; }
// If the X/Y of cursor isn't within the shape's bounds can't interact
if(!clickableCheck(x,y,entity)){
continue;
}
console.log('Entity: '+entity);
console.log('Entities Total live: '+entityLive);
console.log('Entities Total overall: '+entityCount);
switch (interaction) {
case INTERACTION.left_click:
break;
default:
break;
}
}
}

@ -0,0 +1,279 @@
// https://stackoverflow.com/questions/25612452/html5-canvas-game-loop-delta-time-calculations
// TODO: Need to fully figure out a 60FPS and also display FPS on top right
let game = {
fps : 60 // ??
,time : 0.0
,start : Date.now()
,frameDuration : 1000 / 60 // Target FPS 60?
,lagOffset : 0
,player : false
}
startGame();
function startGame(){
// Create player
createPlayerEntity(canvas.width/2, canvas.height/2);
// Start game loop
gameLoop();
}
function gameLoop(){
// Check player exists (not dead/undefined by OOB)
if(entities[game.player] === undefined || game.player === false){
console.log('gome');
return false;
}
setTimeout(() => {
requestAnimationFrame(gameLoop, canvas);
}, game.frameDuration);
// Calcuate the time that has elapsed since the last frame
let current = Date.now();
let elapsed = current - game.start;
game.start = current;
game.lagOffset += elapsed;
// Update the frame if the lag counter is greater than or
// equal to the frame duration
while (game.lagOffset >= game.frameDuration){
gameUpdate();
game.lagOffset -= game.frameDuration;
}
// Calculate the lag offset and use it to render the sprites
// var lagOffset = Math.round(game.lagOffset / game.frameDuration);
// drawEntities(); // May need to pass lagOffset here to attempt to correct positions, etc.
drawGame();
// Print the game version above everything
printText(GAME_VERSION,
20,
canvas.height - 20,
'left', 'alphabetic', 'normal', 'bold', '10', 'Arial', '#00000050'
);
printText(elapsed,
canvas.width - 20 - (ctx.measureText(elapsed).width),
20 + 10, // + 10 is pt font-size to get in right pos
'left', 'alphabetic', 'normal', 'bold', '10', 'Arial', '#00000090'
);
}
// All the game update logic
function gameUpdate(){
calculatePlayerMovement();
calculateDirectionToPlayer();
updateMovement();
updateOOB();
if(MOUSE.down){
createBulkEntities(MOUSE.x, MOUSE.y);
}
}
function calculatePlayerMovement(){
let speedNow = speed[game.player];
if(KEYPRESS.shift){ speedNow = speedNow * 5; }
if(KEYPRESS.w){ position[game.player].y -= (1*speedNow); }
if(KEYPRESS.s){ position[game.player].y += (1*speedNow); }
if(KEYPRESS.a){ position[game.player].x -= (1*speedNow); }
if(KEYPRESS.d){ position[game.player].x += (1*speedNow); }
}
function updateMovement(){
// Use the direction currently, but likely 'velocity' in the future
for (const [entity] of Object.entries(direction)) {
if(position[entity] === undefined){ continue; }
if(direction[entity].x !== undefined){
if(direction[entity].x == 1){
position[entity].x += (1 * speed[entity]);
}
else if(direction[entity].x == -1){
position[entity].x -= (1 * speed[entity]);
}
}
if(direction[entity].y !== undefined){
if(direction[entity].y == 1){
position[entity].y += (1 * speed[entity]);
}
else if(direction[entity].y == -1){
position[entity].y -= (1 * speed[entity]);
}
}
}
}
function calculateDirectionToPlayer(){
// Enemies will W key towards the player as a hoard
for (const [entity] of Object.entries(position)) {
if(entity == game.player){ continue; }
if(position[game.player].x > position[entity].x){ direction[entity].x = 1; }
else{ direction[entity].x = -1; }
if(position[game.player].y > position[entity].y){ direction[entity].y = 1; }
else{ direction[entity].y = -1; }
}
}
function updateOOB(){
for (const [entity] of Object.entries(position)) {
// Remove the entity if it goes out of bounds
if(
position[entity].y >= canvas.height-50 || position[entity].y <= 50
||
position[entity].x >= canvas.width-50 || position[entity].x <= 50
){
deleteEntity(entity);
}
}
}
function deleteEntity(entity){
// Remove from each component
// Definitely the most inefficient and janky way, but it does it
if(position[entity] !== null){ delete position[entity]; }
if(size[entity] !== null){ delete size[entity]; }
if(team.good[entity] !== null){ delete team.good[entity]; }
if(team.bad[entity] !== null){ delete team.bad[entity]; }
if(team.allied[entity] !== null){ delete team.allied[entity]; }
if(direction[entity] !== null){ delete direction[entity]; }
// Finally remove the entity itself
delete entities[entity];
entityLive--; // To keep track of how many active entities there are (just a count)
}
function drawGame(){
// Reset/clear all prior draws
ctx.clearRect(0, 0, canvas.width, canvas.height);
drawEntities();
// Draw 'temp' hitbox area (to visualise entity removal)
drawRectangle(50,50,canvas.width-100,canvas.height-100,null,'#FFF');
}
// Draw the game's entities
function drawEntities(){
// Draw all entities (with size/position)
for (const entity in size) {
// If there's no position (or size), skip
if(position[entity] === undefined){ continue; }
// TEMP z-index draw to ensure player, etc. are drawn atop enemies (for now)
// 0,1,2 TODO: WHY ISN'T IT WOKRIINNNGG
for(let i = 0; i < 2; i++){
// JANK IF. Change to defensive when I can be bothered
if((zindex[entity] === undefined && i === 0) || zindex[entity] === i){
// console.log(zindex[entity]);
if(team.bad[entity] !== undefined){
// Draw pixels from imageCanvas
drawEnemy(entity);
}
if(team.allied[entity] !== undefined){
// Draw pixels from imageCanvas
drawAlly(entity);
}
// Player, temp
if(team.good[entity] !== undefined){
// Draw pixels from imageCanvas
drawPlayer(entity);
}
}
}
}
}
function createTestEntity(x,y){
entityCount++;
entityLive++; // To keep track of how many active entities there are (just a count)
entities[entityCount] = entityCount;
position[entityCount] = {x: x, y: y};
size[entityCount] = {width: 16, height: 16};
if(MOUSE.button[0]){
team.bad[entityCount] = true;
}
else if(MOUSE.button[1]){
team.allied[entityCount] = true;
}
else if(MOUSE.button[2]){
//
}
direction[entityCount] = {x: 0, y: 0}; // 0,1 left,right/up,down
zindex[entityCount] = 0;
speed[entityCount] = .75;
}
function createBulkEntities(x,y){
let amount = 90;
for(let i = 0; i < amount; i++){
createTestEntity(
x+(Math.floor(Math.random() * 45)+1)
,y+Math.floor(Math.random() * 35)+1
);
}
}
function createPlayerEntity(x,y){
entityCount++;
game.player = entityCount; // Set game.player to the player entity. TEMP
entityLive++; // To keep track of how many active entities there are (just a count)
entities[entityCount] = entityCount;
position[entityCount] = {x: x, y: y};
size[entityCount] = {width: 16, height: 16};
team.good[entityCount] = true;
direction[entityCount] = {x: 0, y: 0}; // 0,1 left,right/up,down
zindex[entityCount] = 1;
speed[entityCount] = 1.6;
}

@ -0,0 +1,96 @@
let defaultFillStyle = '#FF0';
let shapeDebug = true;
let shapeDebugColour = '#FF00FF';
let lineWidth = 1;
// Don't think I'll need/want this too often now
// keeping for UI elements that will draw less regular
// but no longer for entities
function drawRectangle(x,y,w,h,colour = false, strokeColour = false){
if(!colour && colour !== null){ colour = defaultFillStyle; }
ctx.fillStyle = colour;
if(colour !== null){
ctx.fillRect(x,y,w,h);
}
ctx.strokeStyle = strokeColour;
ctx.lineWidth = lineWidth;
ctx.strokeRect(x,y,w,h);
if(shapeDebug && !strokeColour){
ctx.strokeStyle = shapeDebugColour;
ctx.lineWidth = lineWidth;
ctx.strokeRect(x,y,w,h);
}
}
function drawCircle(x,y,w,colour = false, strokeColour = false){
if(!colour){ colour = defaultFillStyle; }
ctx.fillStyle = colour;
ctx.beginPath();
ctx.arc(x,y+(w/2), w/2, 0, 2 * Math.PI); // y+(w/2) to align like rect as circle draws from centre
ctx.fill();
if(shapeDebug){
ctx.strokeStyle = shapeDebugColour;
ctx.lineWidth = lineWidth;
ctx.stroke();
}
ctx.closePath();
}
function drawSemiCircle(x,y,w,colour = false, strokeColour = false){
if(!colour){ colour = defaultFillStyle; }
ctx.fillStyle = colour;
ctx.beginPath();
ctx.arc(x,y+(w/2), w/2, Math.PI, 0);
ctx.fill();
if(shapeDebug){
ctx.strokeStyle = shapeDebugColour;
ctx.lineWidth = lineWidth;
ctx.stroke();
}
ctx.closePath();
}
// Shape references from image canvas to be blitted to
function drawEnemy(entity){
ctx.drawImage(imageCanvas,
// source x,y,w,h
0,0,16,16
// x,y,w,h to be drawn onto the (visibile/game) canvas
,position[entity].x,position[entity].y,size[entity].width,size[entity].height
);
}
function drawAlly(entity){
ctx.drawImage(imageCanvas,
// source x,y,w,h
16,0,16,16
// x,y,w,h to be drawn onto the (visibile/game) canvas
,position[entity].x,position[entity].y,size[entity].width,size[entity].height
);
}
function drawPlayer(entity){
ctx.drawImage(imageCanvas,
// source x,y,w,h
32,0,16,16
// x,y,w,h to be drawn onto the (visibile/game) canvas
,position[entity].x,position[entity].y,size[entity].width,size[entity].height
);
}

@ -0,0 +1,62 @@
// WIP: Draw to another canvas then reference that shape
// takes waay less resource
// Draw image from 'temp canvas' to "0,0" on primary canvas
// Will do this to draw each sprite onto the temp canvas, then reference
// the canvas for 'pixel manipulation' or whatnot, see how it goes
imageCanvas = document.createElement("canvas"),
ictx = imageCanvas.getContext("2d");
document.getElementById('canvasRule').parentNode.insertBefore(imageCanvas, document.getElementById('canvasRule'));
// set the canvas to the size of the image(s) with gap, smaller = better
sprites = 3;
imageCanvas.width = 16*sprites;
imageCanvas.height = 16;
addEnemySprite();
addAlliedSprite();
addPlayerSprite();
// Doing all the sprites, etc as 16*16 with 0 gap between
// essentially just a spritesheet on the canvas
function addEnemySprite(){
ictx.fillStyle = '#FF0';
ictx.fillRect(0,0,16,16);
ictx.strokeStyle = '#FF00FF';
ictx.lineWidth = 1;
ictx.strokeRect(0,0,16,16);
}
function addAlliedSprite(){
ictx.fillStyle = '#0EF';
ictx.beginPath();
// awkward cirlce maffs
ictx.arc(16+(16/2),16/2, 15/2, 0, 2 * Math.PI); // y+(w/2) to align like rect as circle draws from centre
ictx.fill();
ictx.strokeStyle = '#FF00FF';
ictx.lineWidth = 1;
ictx.stroke();
ictx.closePath();
}
function addPlayerSprite(){
ictx.fillStyle = '#5ce65c';
ictx.beginPath();
// awkward cirlce maffs
ictx.arc(32+(16/2),16/2, 15/2, 0, 2 * Math.PI); // y+(w/2) to align like rect as circle draws from centre
ictx.fill();
ictx.strokeStyle = '#FF00FF';
ictx.lineWidth = 1;
ictx.stroke();
ictx.closePath();
}
Loading…
Cancel
Save