improved webGL shader, added label shader
This commit is contained in:
parent
4214ef7fd4
commit
924469a2cd
77
dist/builtin/shaders/label.fshader
vendored
Normal file
77
dist/builtin/shaders/label.fshader
vendored
Normal file
|
@ -0,0 +1,77 @@
|
|||
precision mediump float;
|
||||
|
||||
uniform vec4 u_BackgroundColor;
|
||||
uniform vec4 u_BorderColor;
|
||||
uniform float u_BorderWidth;
|
||||
uniform float u_BorderRadius;
|
||||
uniform vec2 u_MaxSize;
|
||||
|
||||
varying vec4 v_Position;
|
||||
|
||||
void main(){
|
||||
vec2 adj_MaxSize = u_MaxSize - u_BorderWidth;
|
||||
vec2 rad_MaxSize = u_MaxSize - u_BorderRadius;
|
||||
vec2 rad2_MaxSize = u_MaxSize - 2.0*u_BorderRadius;
|
||||
|
||||
bool inX = (v_Position.x < adj_MaxSize.x) && (v_Position.x > -adj_MaxSize.x);
|
||||
bool inY = (v_Position.y < adj_MaxSize.y) && (v_Position.y > -adj_MaxSize.y);
|
||||
|
||||
bool inRadiusRangeX = (v_Position.x < rad_MaxSize.x) && (v_Position.x > -rad_MaxSize.x);
|
||||
bool inRadiusRangeY = (v_Position.y < rad_MaxSize.y) && (v_Position.y > -rad_MaxSize.y);
|
||||
|
||||
bool inRadius2RangeX = (v_Position.x < rad2_MaxSize.x) && (v_Position.x > -rad2_MaxSize.x);
|
||||
bool inRadius2RangeY = (v_Position.y < rad2_MaxSize.y) && (v_Position.y > -rad2_MaxSize.y);
|
||||
|
||||
if(inX && inY){
|
||||
// Inside bounds, draw background color
|
||||
gl_FragColor = u_BackgroundColor;
|
||||
} else {
|
||||
// In boundary, draw border color
|
||||
gl_FragColor = u_BorderColor;
|
||||
}
|
||||
|
||||
// This isn't working well right now
|
||||
/*
|
||||
if(inRadius2RangeX || inRadius2RangeY){
|
||||
// Draw normally
|
||||
if(inX && inY){
|
||||
// Inside bounds, draw background color
|
||||
gl_FragColor = u_BackgroundColor;
|
||||
} else {
|
||||
// In boundary, draw border color
|
||||
gl_FragColor = u_BorderColor;
|
||||
}
|
||||
} else if(inRadiusRangeX || inRadiusRangeY){
|
||||
// Draw a rounded boundary for the inner part
|
||||
float x = v_Position.x - sign(v_Position.x)*rad2_MaxSize.x;
|
||||
float y = v_Position.y - sign(v_Position.y)*rad2_MaxSize.y;
|
||||
|
||||
float radSq = x*x + y*y;
|
||||
float bRadSq = u_BorderRadius*u_BorderRadius;
|
||||
|
||||
if(radSq > bRadSq){
|
||||
// Outside of radius - draw as transparent
|
||||
gl_FragColor = u_BorderColor;
|
||||
} else {
|
||||
gl_FragColor = u_BackgroundColor;
|
||||
}
|
||||
} else {
|
||||
// Both coordinates are in the circular section
|
||||
float x = v_Position.x - sign(v_Position.x)*rad_MaxSize.x;
|
||||
float y = v_Position.y - sign(v_Position.y)*rad_MaxSize.y;
|
||||
|
||||
float radSq = x*x + y*y;
|
||||
float bRadSq = u_BorderRadius*u_BorderRadius;
|
||||
|
||||
if(radSq > bRadSq){
|
||||
// Outside of radius - draw as transparent
|
||||
gl_FragColor = vec4(0.0, 0.0, 0.0, 0.0);
|
||||
} else if(sqrt(bRadSq) - sqrt(radSq) < u_BorderWidth) {
|
||||
// In border
|
||||
gl_FragColor = u_BorderColor;
|
||||
} else {
|
||||
gl_FragColor = u_BackgroundColor;
|
||||
}
|
||||
}
|
||||
*/
|
||||
}
|
12
dist/builtin/shaders/label.vshader
vendored
Normal file
12
dist/builtin/shaders/label.vshader
vendored
Normal file
|
@ -0,0 +1,12 @@
|
|||
attribute vec4 a_Position;
|
||||
|
||||
uniform mat4 u_Transform;
|
||||
|
||||
varying vec4 v_Position;
|
||||
|
||||
void main(){
|
||||
gl_Position = u_Transform * a_Position;
|
||||
|
||||
// Pass position to the fragment shader
|
||||
v_Position = a_Position;
|
||||
}
|
24
dist/hw1_assets/shaders/gradient_circle.fshader
vendored
24
dist/hw1_assets/shaders/gradient_circle.fshader
vendored
|
@ -1,24 +0,0 @@
|
|||
precision mediump float;
|
||||
|
||||
uniform vec4 u_Color;
|
||||
|
||||
varying vec4 v_Position;
|
||||
|
||||
void main(){
|
||||
// Default alpha is 0
|
||||
float alpha = 0.0;
|
||||
|
||||
// Radius is 0.5, since the diameter of our quad is 1
|
||||
float radius = 0.5;
|
||||
|
||||
// Get the distance squared of from (0, 0)
|
||||
float dist_sq = v_Position.x*v_Position.x + v_Position.y*v_Position.y;
|
||||
|
||||
if(dist_sq < radius*radius){
|
||||
// Multiply by 4, since distance squared is at most 0.25
|
||||
alpha = 4.0*dist_sq;
|
||||
}
|
||||
|
||||
// Use the alpha value in our color
|
||||
gl_FragColor = vec4(u_Color.rgb, alpha);
|
||||
}
|
11
dist/hw1_assets/shaders/gradient_circle.vshader
vendored
11
dist/hw1_assets/shaders/gradient_circle.vshader
vendored
|
@ -1,11 +0,0 @@
|
|||
attribute vec4 a_Position;
|
||||
|
||||
uniform mat4 u_Transform;
|
||||
|
||||
varying vec4 v_Position;
|
||||
|
||||
void main(){
|
||||
gl_Position = u_Transform * a_Position;
|
||||
|
||||
v_Position = a_Position;
|
||||
}
|
145
dist/hw1_assets/spritesheets/player_spaceship.json
vendored
145
dist/hw1_assets/spritesheets/player_spaceship.json
vendored
|
@ -1,145 +0,0 @@
|
|||
{
|
||||
"name": "player_spaceship",
|
||||
"spriteSheetImage": "player_spaceship.png",
|
||||
"spriteWidth": 256,
|
||||
"spriteHeight": 256,
|
||||
"leftBuffer": 0,
|
||||
"rightBuffer": 0,
|
||||
"topBuffer": 0,
|
||||
"bottomBuffer": 0,
|
||||
"columns": 5,
|
||||
"rows": 5,
|
||||
"animations": [
|
||||
{
|
||||
"name": "idle",
|
||||
"repeat": true,
|
||||
"frames": [
|
||||
{
|
||||
"index": 0,
|
||||
"duration": 10
|
||||
},
|
||||
{
|
||||
"index": 1,
|
||||
"duration": 10
|
||||
},
|
||||
{
|
||||
"index": 2,
|
||||
"duration": 10
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "boost",
|
||||
"repeat": true,
|
||||
"frames": [
|
||||
{
|
||||
"index": 3,
|
||||
"duration": 10
|
||||
},
|
||||
{
|
||||
"index": 4,
|
||||
"duration": 10
|
||||
},
|
||||
{
|
||||
"index": 5,
|
||||
"duration": 10
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "shield",
|
||||
"repeat": false,
|
||||
"frames": [
|
||||
{
|
||||
"index": 6,
|
||||
"duration": 10
|
||||
},
|
||||
{
|
||||
"index": 7,
|
||||
"duration": 10
|
||||
},
|
||||
{
|
||||
"index": 8,
|
||||
"duration": 10
|
||||
},
|
||||
{
|
||||
"index": 9,
|
||||
"duration": 10
|
||||
},
|
||||
{
|
||||
"index": 10,
|
||||
"duration": 10
|
||||
},
|
||||
{
|
||||
"index": 11,
|
||||
"duration": 10
|
||||
},
|
||||
{
|
||||
"index": 12,
|
||||
"duration": 10
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "explode",
|
||||
"repeat": false,
|
||||
"frames": [
|
||||
{
|
||||
"index": 13,
|
||||
"duration": 10
|
||||
},
|
||||
{
|
||||
"index": 14,
|
||||
"duration": 10
|
||||
},
|
||||
{
|
||||
"index": 15,
|
||||
"duration": 10
|
||||
},
|
||||
{
|
||||
"index": 16,
|
||||
"duration": 10
|
||||
},
|
||||
{
|
||||
"index": 17,
|
||||
"duration": 10
|
||||
},
|
||||
{
|
||||
"index": 18,
|
||||
"duration": 10
|
||||
},
|
||||
{
|
||||
"index": 19,
|
||||
"duration": 10
|
||||
},
|
||||
{
|
||||
"index": 20,
|
||||
"duration": 10
|
||||
},
|
||||
{
|
||||
"index": 21,
|
||||
"duration": 10
|
||||
},
|
||||
{
|
||||
"index": 22,
|
||||
"duration": 10
|
||||
},
|
||||
{
|
||||
"index": 23,
|
||||
"duration": 10
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "explode",
|
||||
"repeat": false,
|
||||
"onEnd": "dead",
|
||||
"frames": [
|
||||
{
|
||||
"index": 24,
|
||||
"duration": 1
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
BIN
dist/hw1_assets/spritesheets/player_spaceship.png
vendored
BIN
dist/hw1_assets/spritesheets/player_spaceship.png
vendored
Binary file not shown.
Before Width: | Height: | Size: 66 KiB |
|
@ -1,48 +0,0 @@
|
|||
import Vec2 from "./Wolfie2D/DataTypes/Vec2";
|
||||
import { GraphicType } from "./Wolfie2D/Nodes/Graphics/GraphicTypes";
|
||||
import Point from "./Wolfie2D/Nodes/Graphics/Point";
|
||||
import Rect from "./Wolfie2D/Nodes/Graphics/Rect";
|
||||
import AnimatedSprite from "./Wolfie2D/Nodes/Sprites/AnimatedSprite";
|
||||
import Sprite from "./Wolfie2D/Nodes/Sprites/Sprite";
|
||||
import Scene from "./Wolfie2D/Scene/Scene";
|
||||
import Color from "./Wolfie2D/Utils/Color";
|
||||
|
||||
export default class WebGLScene extends Scene {
|
||||
|
||||
private point: Point;
|
||||
private rect: Rect;
|
||||
private player: AnimatedSprite;
|
||||
private t: number = 0;
|
||||
|
||||
loadScene() {
|
||||
this.load.spritesheet("player", "hw1_assets/spritesheets/player_spaceship.json");
|
||||
}
|
||||
|
||||
startScene() {
|
||||
this.addLayer("primary");
|
||||
|
||||
this.point = this.add.graphic(GraphicType.POINT, "primary", {position: new Vec2(100, 100), size: new Vec2(10, 10)})
|
||||
this.point.color = Color.CYAN;
|
||||
console.log(this.point.color.toStringRGBA());
|
||||
|
||||
this.rect = <Rect>this.add.graphic(GraphicType.RECT, "primary", {position: new Vec2(300, 100), size: new Vec2(100, 50)});
|
||||
this.rect.color = Color.ORANGE;
|
||||
|
||||
this.player = this.add.animatedSprite("player", "primary");
|
||||
this.player.position.set(800, 500);
|
||||
this.player.scale.set(0.5, 0.5);
|
||||
this.player.animation.play("idle");
|
||||
}
|
||||
|
||||
updateScene(deltaT: number) {
|
||||
this.t += deltaT;
|
||||
|
||||
let s = Math.sin(this.t);
|
||||
let c = Math.cos(this.t);
|
||||
|
||||
this.point.position.x = 100 + 100*c;
|
||||
this.point.position.y = 100 + 100*s;
|
||||
|
||||
this.rect.rotation = this.t;
|
||||
}
|
||||
}
|
|
@ -1,18 +0,0 @@
|
|||
import AI from "../DataTypes/Interfaces/AI";
|
||||
import GameEvent from "../Events/GameEvent";
|
||||
import GameNode from "../Nodes/GameNode";
|
||||
|
||||
/**
|
||||
* A very basic AI class that just runs a function every update
|
||||
*/
|
||||
export default class ControllerAI implements AI {
|
||||
protected owner: GameNode;
|
||||
|
||||
initializeAI(owner: GameNode, options: Record<string, any>): void {
|
||||
this.owner = owner;
|
||||
}
|
||||
|
||||
handleEvent(event: GameEvent): void {}
|
||||
|
||||
update(deltaT: number): void {}
|
||||
}
|
|
@ -12,4 +12,6 @@ export default class StateMachineAI extends StateMachine implements AI {
|
|||
|
||||
// @implemented
|
||||
initializeAI(owner: GameNode, config: Record<string, any>): void {}
|
||||
|
||||
activate(options: Record<string, any>): void {}
|
||||
}
|
|
@ -10,6 +10,9 @@ export default interface AI extends Updateable {
|
|||
/** Initializes the AI with the actor and any additional config */
|
||||
initializeAI(owner: GameNode, options: Record<string, any>): void;
|
||||
|
||||
/** Activates this AI from a stopped state and allows variables to be passed in */
|
||||
activate(options: Record<string, any>): void;
|
||||
|
||||
/** Handles events from the Actor */
|
||||
handleEvent(event: GameEvent): void;
|
||||
}
|
|
@ -30,6 +30,7 @@ export default interface Actor {
|
|||
/**
|
||||
* Sets the AI to start/stop for this Actor.
|
||||
* @param active The new active status of the AI.
|
||||
* @param options An object that allows options to be pased to the activated AI
|
||||
*/
|
||||
setAIActive(active: boolean): void;
|
||||
setAIActive(active: boolean, options: Record<string, any>): void;
|
||||
}
|
|
@ -23,6 +23,26 @@ export default class AABB extends Shape {
|
|||
this.halfSize = halfSize ? halfSize : new Vec2(0, 0);
|
||||
}
|
||||
|
||||
/** Returns a point representing the top left corner of the AABB */
|
||||
get topLeft(): Vec2 {
|
||||
return new Vec2(this.left, this.top)
|
||||
}
|
||||
|
||||
/** Returns a point representing the top right corner of the AABB */
|
||||
get topRight(): Vec2 {
|
||||
return new Vec2(this.right, this.top)
|
||||
}
|
||||
|
||||
/** Returns a point representing the bottom left corner of the AABB */
|
||||
get bottomLeft(): Vec2 {
|
||||
return new Vec2(this.left, this.bottom)
|
||||
}
|
||||
|
||||
/** Returns a point representing the bottom right corner of the AABB */
|
||||
get bottomRight(): Vec2 {
|
||||
return new Vec2(this.right, this.bottom)
|
||||
}
|
||||
|
||||
// @override
|
||||
getBoundingRect(): AABB {
|
||||
return this.clone();
|
||||
|
|
|
@ -7,7 +7,7 @@ import Shape from "./Shape";
|
|||
*/
|
||||
export default class Circle extends Shape {
|
||||
private _center: Vec2;
|
||||
private radius: number;
|
||||
radius: number;
|
||||
|
||||
/**
|
||||
* Creates a new Circle
|
||||
|
@ -32,6 +32,24 @@ export default class Circle extends Shape {
|
|||
return new Vec2(this.radius, this.radius);
|
||||
}
|
||||
|
||||
get r(): number {
|
||||
return this.radius;
|
||||
}
|
||||
|
||||
set r(radius: number) {
|
||||
this.radius = radius;
|
||||
}
|
||||
|
||||
// @override
|
||||
/**
|
||||
* A simple boolean check of whether this AABB contains a point
|
||||
* @param point The point to check
|
||||
* @returns A boolean representing whether this AABB contains the specified point
|
||||
*/
|
||||
containsPoint(point: Vec2): boolean {
|
||||
return this.center.distanceSqTo(point) <= this.radius*this.radius;
|
||||
}
|
||||
|
||||
// @override
|
||||
getBoundingRect(): AABB {
|
||||
return new AABB(this._center.clone(), new Vec2(this.radius, this.radius));
|
||||
|
@ -51,4 +69,8 @@ export default class Circle extends Shape {
|
|||
clone(): Circle {
|
||||
return new Circle(this._center.clone(), this.radius);
|
||||
}
|
||||
|
||||
toString(): string {
|
||||
return "(center: " + this.center.toString() + ", radius: " + this.radius + ")";
|
||||
}
|
||||
}
|
|
@ -71,6 +71,13 @@ export default abstract class Shape {
|
|||
*/
|
||||
abstract overlaps(other: Shape): boolean;
|
||||
|
||||
/**
|
||||
* A simple boolean check of whether this Shape contains a point
|
||||
* @param point The point to check
|
||||
* @returns A boolean representing whether this Shape contains the specified point
|
||||
*/
|
||||
abstract containsPoint(point: Vec2): boolean;
|
||||
|
||||
static getTimeOfCollision(A: Shape, velA: Vec2, B: Shape, velB: Vec2): [Vec2, Vec2, boolean, boolean] {
|
||||
if(A instanceof AABB && B instanceof AABB){
|
||||
return Shape.getTimeOfCollision_AABB_AABB(A, velA, B, velB);
|
||||
|
|
|
@ -269,6 +269,17 @@ export default class Vec2 {
|
|||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Does an element wise remainder operation on this vector. this.x %= other.x and this.y %= other.y
|
||||
* @param other The other vector
|
||||
* @returns this vector
|
||||
*/
|
||||
remainder(other: Vec2): Vec2 {
|
||||
this.x = this.x % other.x;
|
||||
this.y = this.y % other.y;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the squared distance between this vector and another vector
|
||||
* @param other The vector to compute distance squared to
|
||||
|
|
|
@ -78,6 +78,36 @@ export default class Debug {
|
|||
this.debugRenderingContext.globalAlpha = alpha;
|
||||
}
|
||||
|
||||
/**
|
||||
* Draws a circle at the specified position
|
||||
* @param center The center of the circle
|
||||
* @param radius The dimensions of the box
|
||||
* @param filled A boolean for whether or not the circle is filled
|
||||
* @param color The color of the circle
|
||||
*/
|
||||
static drawCircle(center: Vec2, radius: number, filled: boolean, color: Color): void {
|
||||
let alpha = this.debugRenderingContext.globalAlpha;
|
||||
this.debugRenderingContext.globalAlpha = color.a;
|
||||
|
||||
if(filled){
|
||||
this.debugRenderingContext.fillStyle = color.toString();
|
||||
this.debugRenderingContext.beginPath();
|
||||
this.debugRenderingContext.arc(center.x, center.y, radius, 0, 2 * Math.PI);
|
||||
this.debugRenderingContext.closePath();
|
||||
this.debugRenderingContext.fill();
|
||||
} else {
|
||||
let lineWidth = 2;
|
||||
this.debugRenderingContext.lineWidth = lineWidth;
|
||||
this.debugRenderingContext.strokeStyle = color.toString();
|
||||
this.debugRenderingContext.beginPath();
|
||||
this.debugRenderingContext.arc(center.x, center.y, radius, 0, 2 * Math.PI);
|
||||
this.debugRenderingContext.closePath();
|
||||
this.debugRenderingContext.stroke();
|
||||
}
|
||||
|
||||
this.debugRenderingContext.globalAlpha = alpha;
|
||||
}
|
||||
|
||||
/**
|
||||
* Draws a ray at the specified position
|
||||
* @param from The starting position of the ray
|
||||
|
|
|
@ -18,6 +18,7 @@ import EnvironmentInitializer from "./EnvironmentInitializer";
|
|||
import Vec2 from "../DataTypes/Vec2";
|
||||
import Registry from "../Registry/Registry";
|
||||
import WebGLRenderer from "../Rendering/WebGLRenderer";
|
||||
import Scene from "../Scene/Scene";
|
||||
|
||||
/**
|
||||
* The main loop of the game engine.
|
||||
|
@ -130,7 +131,7 @@ export default class Game {
|
|||
/**
|
||||
* Starts the game
|
||||
*/
|
||||
start(): void {
|
||||
start(InitialScene: new (...args: any) => Scene, options: Record<string, any>): void {
|
||||
// Set the update function of the loop
|
||||
this.loop.doUpdate = (deltaT: number) => this.update(deltaT);
|
||||
|
||||
|
@ -142,7 +143,9 @@ export default class Game {
|
|||
|
||||
// Load the items with the resource manager
|
||||
this.resourceManager.loadResourcesFromQueue(() => {
|
||||
// When we're dont loading, start the loop
|
||||
// When we're done loading, start the loop
|
||||
console.log("Finished Preload - loading first scene");
|
||||
this.sceneManager.addScene(InitialScene, options);
|
||||
this.loop.start();
|
||||
});
|
||||
}
|
||||
|
@ -178,14 +181,7 @@ export default class Game {
|
|||
// Clear the canvases
|
||||
Debug.clearCanvas();
|
||||
|
||||
if(this.gameOptions.useWebGL){
|
||||
(<WebGLRenderingContext>this.ctx).clearColor(this.clearColor.r, this.clearColor.g, this.clearColor.b, this.clearColor.a);
|
||||
(<WebGLRenderingContext>this.ctx).clear((<WebGLRenderingContext>this.ctx).COLOR_BUFFER_BIT | (<WebGLRenderingContext>this.ctx).DEPTH_BUFFER_BIT);
|
||||
} else {
|
||||
(<CanvasRenderingContext2D>this.ctx).clearRect(0, 0, this.WIDTH, this.HEIGHT);
|
||||
(<CanvasRenderingContext2D>this.ctx).fillStyle = this.clearColor.toString();
|
||||
(<CanvasRenderingContext2D>this.ctx).fillRect(0, 0, this.WIDTH, this.HEIGHT);
|
||||
}
|
||||
this.renderingManager.clear(this.clearColor);
|
||||
|
||||
this.sceneManager.render();
|
||||
|
||||
|
|
|
@ -18,6 +18,7 @@ import NavigationPath from "../Pathfinding/NavigationPath";
|
|||
import TweenManager from "../Rendering/Animations/TweenManager";
|
||||
import Debug from "../Debug/Debug";
|
||||
import Color from "../Utils/Color";
|
||||
import Circle from "../DataTypes/Shapes/Circle";
|
||||
|
||||
/**
|
||||
* The representation of an object in the game world.
|
||||
|
@ -198,6 +199,15 @@ export default abstract class GameNode implements Positioned, Unique, Updateable
|
|||
this.scene.getPhysicsManager().registerObject(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the collider for this GameNode
|
||||
* @param collider The new collider to use
|
||||
*/
|
||||
setCollisionShape(collider: Shape): void {
|
||||
this.collisionShape = collider;
|
||||
this.collisionShape.center.copy(this.position);
|
||||
}
|
||||
|
||||
// @implemented
|
||||
/**
|
||||
* @param group The name of the group that will activate the trigger
|
||||
|
@ -254,8 +264,9 @@ export default abstract class GameNode implements Positioned, Unique, Updateable
|
|||
}
|
||||
|
||||
// @implemented
|
||||
setAIActive(active: boolean): void {
|
||||
setAIActive(active: boolean, options: Record<string, any>): void {
|
||||
this.aiActive = active;
|
||||
this.ai.activate(options);
|
||||
}
|
||||
|
||||
/*---------- TWEENABLE PROPERTIES ----------*/
|
||||
|
@ -306,8 +317,13 @@ export default abstract class GameNode implements Positioned, Unique, Updateable
|
|||
|
||||
/** Called if the position vector is modified or replaced */
|
||||
protected positionChanged(): void {
|
||||
if(this.hasPhysics){
|
||||
if(this.collisionShape){
|
||||
if(this.colliderOffset){
|
||||
this.collisionShape.center = this.position.clone().add(this.colliderOffset);
|
||||
} else {
|
||||
this.collisionShape.center = this.position.clone();
|
||||
}
|
||||
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -336,15 +352,20 @@ export default abstract class GameNode implements Positioned, Unique, Updateable
|
|||
}
|
||||
|
||||
// If this has a collider, draw it
|
||||
if(this.hasPhysics && this.collisionShape){
|
||||
if(this.collisionShape){
|
||||
let color = this.isColliding ? Color.RED : Color.GREEN;
|
||||
|
||||
if(this.isTrigger){
|
||||
color = Color.PURPLE;
|
||||
color = Color.MAGENTA;
|
||||
}
|
||||
|
||||
color.a = 0.2;
|
||||
|
||||
if(this.collisionShape instanceof AABB){
|
||||
Debug.drawBox(this.inRelativeCoordinates(this.collisionShape.center), this.collisionShape.halfSize.scaled(this.scene.getViewScale()), true, color);
|
||||
} else if(this.collisionShape instanceof Circle){
|
||||
Debug.drawCircle(this.inRelativeCoordinates(this.collisionShape.center), this.collisionShape.hw*this.scene.getViewScale(), true, color);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -82,7 +82,7 @@ export default abstract class UIElement extends CanvasNode {
|
|||
// See of this object was just clicked
|
||||
if(Input.isMouseJustPressed()){
|
||||
let clickPos = Input.getMousePressPosition();
|
||||
if(this.contains(clickPos.x, clickPos.y)){
|
||||
if(this.contains(clickPos.x, clickPos.y) && this.visible && !this.layer.isHidden()){
|
||||
this.isClicked = true;
|
||||
|
||||
if(this.onClick !== null){
|
||||
|
@ -136,15 +136,15 @@ export default abstract class UIElement extends CanvasNode {
|
|||
* Overridable method for calculating background color - useful for elements that want to be colored on different after certain events
|
||||
* @returns The background color of the UIElement
|
||||
*/
|
||||
calculateBackgroundColor(): string {
|
||||
return this.backgroundColor.toStringRGBA();
|
||||
calculateBackgroundColor(): Color {
|
||||
return this.backgroundColor;
|
||||
}
|
||||
|
||||
/**
|
||||
* Overridable method for calculating border color - useful for elements that want to be colored on different after certain events
|
||||
* @returns The border color of the UIElement
|
||||
*/
|
||||
calculateBorderColor(): string {
|
||||
return this.borderColor.toStringRGBA();
|
||||
calculateBorderColor(): Color {
|
||||
return this.borderColor;
|
||||
}
|
||||
}
|
|
@ -14,14 +14,14 @@ export default class Button extends Label {
|
|||
}
|
||||
|
||||
// @override
|
||||
calculateBackgroundColor(): string {
|
||||
calculateBackgroundColor(): Color {
|
||||
// Change the background color if clicked or hovered
|
||||
if(this.isEntered && !this.isClicked){
|
||||
return this.backgroundColor.lighten().toStringRGBA();
|
||||
return this.backgroundColor.lighten();
|
||||
} else if(this.isClicked){
|
||||
return this.backgroundColor.darken().toStringRGBA();
|
||||
return this.backgroundColor.darken();
|
||||
} else {
|
||||
return this.backgroundColor.toStringRGBA();
|
||||
return this.backgroundColor;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -68,6 +68,10 @@ export default class Label extends UIElement{
|
|||
return ctx.measureText(this.text).width;
|
||||
}
|
||||
|
||||
setHAlign(align: string): void {
|
||||
this.hAlign = align;
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculate the offset of the text - this is used for rendering text with different alignments
|
||||
* @param ctx The rendering context
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
import Map from "../../DataTypes/Map";
|
||||
import ShaderType from "../../Rendering/WebGLRendering/ShaderType";
|
||||
import LabelShaderType from "../../Rendering/WebGLRendering/ShaderTypes/LabelShaderType";
|
||||
import PointShaderType from "../../Rendering/WebGLRendering/ShaderTypes/PointShaderType";
|
||||
import RectShaderType from "../../Rendering/WebGLRendering/ShaderTypes/RectShaderType";
|
||||
import SpriteShaderType from "../../Rendering/WebGLRendering/ShaderTypes/SpriteShaderType";
|
||||
|
@ -14,6 +15,7 @@ export default class ShaderRegistry extends Map<ShaderType> {
|
|||
public static POINT_SHADER = "point";
|
||||
public static RECT_SHADER = "rect";
|
||||
public static SPRITE_SHADER = "sprite";
|
||||
public static LABEL_SHADER = "label";
|
||||
|
||||
private registryItems: Array<ShaderRegistryItem> = new Array();
|
||||
|
||||
|
@ -21,8 +23,6 @@ export default class ShaderRegistry extends Map<ShaderType> {
|
|||
* Preloads all built-in shaders
|
||||
*/
|
||||
public preload(){
|
||||
console.log("Preloading");
|
||||
|
||||
// Get the resourceManager and queue all built-in shaders for preloading
|
||||
const rm = ResourceManager.getInstance();
|
||||
|
||||
|
@ -35,17 +35,17 @@ export default class ShaderRegistry extends Map<ShaderType> {
|
|||
// Queue a load for the sprite shader
|
||||
this.registerAndPreloadItem(ShaderRegistry.SPRITE_SHADER, SpriteShaderType, "builtin/shaders/sprite.vshader", "builtin/shaders/sprite.fshader");
|
||||
|
||||
// Queue a load for the label shader
|
||||
this.registerAndPreloadItem(ShaderRegistry.LABEL_SHADER, LabelShaderType, "builtin/shaders/label.vshader", "builtin/shaders/label.fshader");
|
||||
|
||||
// Queue a load for any preloaded items
|
||||
for(let item of this.registryItems){
|
||||
const shader = new item.constr(item.key);
|
||||
shader.initBufferObject();
|
||||
this.add(item.key, shader);
|
||||
|
||||
console.log("Added", item.key);
|
||||
|
||||
// Load if desired
|
||||
if(item.preload !== undefined){
|
||||
console.log("Preloading", item.key);
|
||||
rm.shader(item.key, item.preload.vshaderLocation, item.preload.fshaderLocation);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -19,6 +19,7 @@ import Slider from "../Nodes/UIElements/Slider";
|
|||
import TextInput from "../Nodes/UIElements/TextInput";
|
||||
import AnimatedSprite from "../Nodes/Sprites/AnimatedSprite";
|
||||
import Vec2 from "../DataTypes/Vec2";
|
||||
import Color from "../Utils/Color";
|
||||
|
||||
/**
|
||||
* An implementation of the RenderingManager class using CanvasRenderingContext2D.
|
||||
|
@ -32,6 +33,8 @@ export default class CanvasRenderer extends RenderingManager {
|
|||
protected origin: Vec2;
|
||||
protected zoom: number;
|
||||
|
||||
protected worldSize: Vec2;
|
||||
|
||||
constructor(){
|
||||
super();
|
||||
}
|
||||
|
@ -49,6 +52,8 @@ export default class CanvasRenderer extends RenderingManager {
|
|||
canvas.width = width;
|
||||
canvas.height = height;
|
||||
|
||||
this.worldSize = new Vec2(width, height);
|
||||
|
||||
this.ctx = canvas.getContext("2d");
|
||||
|
||||
this.graphicRenderer = new GraphicRenderer(this.ctx);
|
||||
|
@ -107,7 +112,10 @@ export default class CanvasRenderer extends RenderingManager {
|
|||
}
|
||||
|
||||
// Render the uiLayers on top of everything else
|
||||
uiLayers.forEach(key => uiLayers.get(key).getItems().forEach(node => this.renderNode(<CanvasNode>node)));
|
||||
uiLayers.forEach(key => {
|
||||
if(!uiLayers.get(key).isHidden())
|
||||
uiLayers.get(key).getItems().forEach(node => this.renderNode(<CanvasNode>node))
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -221,4 +229,10 @@ export default class CanvasRenderer extends RenderingManager {
|
|||
this.uiElementRenderer.renderTextInput(uiElement);
|
||||
}
|
||||
}
|
||||
|
||||
clear(clearColor: Color): void {
|
||||
this.ctx.clearRect(0, 0, this.worldSize.x, this.worldSize.y);
|
||||
this.ctx.fillStyle = clearColor.toString();
|
||||
this.ctx.fillRect(0, 0, this.worldSize.x, this.worldSize.y);
|
||||
}
|
||||
}
|
|
@ -45,11 +45,11 @@ export default class UIElementRenderer {
|
|||
|
||||
// Stroke and fill a rounded rect and give it text
|
||||
this.ctx.globalAlpha = label.backgroundColor.a;
|
||||
this.ctx.fillStyle = label.calculateBackgroundColor();
|
||||
this.ctx.fillStyle = label.calculateBackgroundColor().toStringRGBA();
|
||||
this.ctx.fillRoundedRect(-label.size.x/2, -label.size.y/2,
|
||||
label.size.x, label.size.y, label.borderRadius);
|
||||
|
||||
this.ctx.strokeStyle = label.calculateBorderColor();
|
||||
this.ctx.strokeStyle = label.calculateBorderColor().toStringRGBA();
|
||||
this.ctx.globalAlpha = label.borderColor.a;
|
||||
this.ctx.lineWidth = label.borderWidth;
|
||||
this.ctx.strokeRoundedRect(-label.size.x/2, -label.size.y/2,
|
||||
|
|
|
@ -8,6 +8,7 @@ import UIElement from "../Nodes/UIElement";
|
|||
import ResourceManager from "../ResourceManager/ResourceManager";
|
||||
import UILayer from "../Scene/Layers/UILayer";
|
||||
import Scene from "../Scene/Scene";
|
||||
import Color from "../Utils/Color";
|
||||
|
||||
/**
|
||||
* An abstract framework to put all rendering in once place in the application
|
||||
|
@ -48,6 +49,9 @@ export default abstract class RenderingManager {
|
|||
*/
|
||||
abstract render(visibleSet: Array<CanvasNode>, tilemaps: Array<Tilemap>, uiLayers: Map<UILayer>): void;
|
||||
|
||||
/** Clears the canvas */
|
||||
abstract clear(color: Color): void;
|
||||
|
||||
/**
|
||||
* Renders a sprite
|
||||
* @param sprite The sprite to render
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
import Graph from "../DataTypes/Graphs/Graph";
|
||||
import Map from "../DataTypes/Map";
|
||||
import Vec2 from "../DataTypes/Vec2";
|
||||
import Debug from "../Debug/Debug";
|
||||
import CanvasNode from "../Nodes/CanvasNode";
|
||||
import Graphic from "../Nodes/Graphic";
|
||||
import { GraphicType } from "../Nodes/Graphics/GraphicTypes";
|
||||
|
@ -10,10 +11,13 @@ import AnimatedSprite from "../Nodes/Sprites/AnimatedSprite";
|
|||
import Sprite from "../Nodes/Sprites/Sprite";
|
||||
import Tilemap from "../Nodes/Tilemap";
|
||||
import UIElement from "../Nodes/UIElement";
|
||||
import Label from "../Nodes/UIElements/Label";
|
||||
import ShaderRegistry from "../Registry/Registries/ShaderRegistry";
|
||||
import Registry from "../Registry/Registry";
|
||||
import ResourceManager from "../ResourceManager/ResourceManager";
|
||||
import ParallaxLayer from "../Scene/Layers/ParallaxLayer";
|
||||
import UILayer from "../Scene/Layers/UILayer";
|
||||
import Color from "../Utils/Color";
|
||||
import RenderingUtils from "../Utils/RenderingUtils";
|
||||
import RenderingManager from "./RenderingManager";
|
||||
import ShaderType from "./WebGLRendering/ShaderType";
|
||||
|
@ -25,6 +29,7 @@ export default class WebGLRenderer extends RenderingManager {
|
|||
protected worldSize: Vec2;
|
||||
|
||||
protected gl: WebGLRenderingContext;
|
||||
protected textCtx: CanvasRenderingContext2D;
|
||||
|
||||
initializeCanvas(canvas: HTMLCanvasElement, width: number, height: number): WebGLRenderingContext {
|
||||
canvas.width = width;
|
||||
|
@ -47,6 +52,15 @@ export default class WebGLRenderer extends RenderingManager {
|
|||
// Tell the resource manager we're using WebGL
|
||||
ResourceManager.getInstance().useWebGL(true, this.gl);
|
||||
|
||||
// Show the text canvas and get its context
|
||||
let textCanvas = <HTMLCanvasElement>document.getElementById("text-canvas");
|
||||
textCanvas.hidden = false;
|
||||
this.textCtx = textCanvas.getContext("2d");
|
||||
|
||||
// Size the text canvas to be the same as the game canvas
|
||||
textCanvas.height = height;
|
||||
textCanvas.width = width;
|
||||
|
||||
return this.gl;
|
||||
}
|
||||
|
||||
|
@ -54,6 +68,18 @@ export default class WebGLRenderer extends RenderingManager {
|
|||
for(let node of visibleSet){
|
||||
this.renderNode(node);
|
||||
}
|
||||
|
||||
uiLayers.forEach(key => {
|
||||
if(!uiLayers.get(key).isHidden())
|
||||
uiLayers.get(key).getItems().forEach(node => this.renderNode(<CanvasNode>node))
|
||||
});
|
||||
}
|
||||
|
||||
clear(color: Color): void {
|
||||
this.gl.clearColor(color.r, color.g, color.b, color.a);
|
||||
this.gl.clear(this.gl.COLOR_BUFFER_BIT | this.gl.DEPTH_BUFFER_BIT);
|
||||
|
||||
this.textCtx.clearRect(0, 0, this.worldSize.x, this.worldSize.y);
|
||||
}
|
||||
|
||||
protected renderNode(node: CanvasNode): void {
|
||||
|
@ -74,44 +100,32 @@ export default class WebGLRenderer extends RenderingManager {
|
|||
} else {
|
||||
this.renderSprite(node);
|
||||
}
|
||||
} else if(node instanceof UIElement){
|
||||
this.renderUIElement(node);
|
||||
}
|
||||
}
|
||||
|
||||
protected renderSprite(sprite: Sprite): void {
|
||||
let shader = Registry.shaders.get(ShaderRegistry.SPRITE_SHADER);
|
||||
|
||||
let options = shader.getOptions(sprite);
|
||||
options.worldSize = this.worldSize;
|
||||
options.origin = this.origin;
|
||||
|
||||
let options = this.addOptions(shader.getOptions(sprite), sprite);
|
||||
shader.render(this.gl, options);
|
||||
}
|
||||
|
||||
protected renderAnimatedSprite(sprite: AnimatedSprite): void {
|
||||
let shader = Registry.shaders.get(ShaderRegistry.SPRITE_SHADER);
|
||||
|
||||
let options = shader.getOptions(sprite);
|
||||
options.worldSize = this.worldSize;
|
||||
options.origin = this.origin;
|
||||
|
||||
Registry.shaders.get(ShaderRegistry.SPRITE_SHADER).render(this.gl, options);
|
||||
let options = this.addOptions(shader.getOptions(sprite), sprite);
|
||||
shader.render(this.gl, options);
|
||||
}
|
||||
|
||||
protected renderGraphic(graphic: Graphic): void {
|
||||
|
||||
if(graphic instanceof Point){
|
||||
let shader = Registry.shaders.get(ShaderRegistry.POINT_SHADER);
|
||||
let options = shader.getOptions(graphic);
|
||||
options.worldSize = this.worldSize;
|
||||
options.origin = this.origin;
|
||||
|
||||
let options = this.addOptions(shader.getOptions(graphic), graphic);
|
||||
shader.render(this.gl, options);
|
||||
} else if(graphic instanceof Rect) {
|
||||
let shader = Registry.shaders.get(ShaderRegistry.RECT_SHADER);
|
||||
let options = shader.getOptions(graphic);
|
||||
options.worldSize = this.worldSize;
|
||||
options.origin = this.origin;
|
||||
|
||||
let options = this.addOptions(shader.getOptions(graphic), graphic);
|
||||
shader.render(this.gl, options);
|
||||
}
|
||||
}
|
||||
|
@ -121,16 +135,48 @@ export default class WebGLRenderer extends RenderingManager {
|
|||
}
|
||||
|
||||
protected renderUIElement(uiElement: UIElement): void {
|
||||
throw new Error("Method not implemented.");
|
||||
if(uiElement instanceof Label){
|
||||
let shader = Registry.shaders.get(ShaderRegistry.LABEL_SHADER);
|
||||
let options = this.addOptions(shader.getOptions(uiElement), uiElement);
|
||||
shader.render(this.gl, options);
|
||||
|
||||
this.textCtx.setTransform(1, 0, 0, 1, (uiElement.position.x - this.origin.x)*this.zoom, (uiElement.position.y - this.origin.y)*this.zoom);
|
||||
this.textCtx.rotate(-uiElement.rotation);
|
||||
let globalAlpha = this.textCtx.globalAlpha;
|
||||
this.textCtx.globalAlpha = uiElement.alpha;
|
||||
|
||||
// Render text
|
||||
this.textCtx.font = uiElement.getFontString();
|
||||
let offset = uiElement.calculateTextOffset(this.textCtx);
|
||||
this.textCtx.fillStyle = uiElement.calculateTextColor();
|
||||
this.textCtx.globalAlpha = uiElement.textColor.a;
|
||||
this.textCtx.fillText(uiElement.text, offset.x - uiElement.size.x/2, offset.y - uiElement.size.y/2);
|
||||
|
||||
this.textCtx.globalAlpha = globalAlpha;
|
||||
this.textCtx.setTransform(1, 0, 0, 1, 0, 0);
|
||||
}
|
||||
}
|
||||
|
||||
protected renderCustom(node: CanvasNode): void {
|
||||
let shader = Registry.shaders.get(node.customShaderKey);
|
||||
let options = shader.getOptions(node);
|
||||
options.worldSize = this.worldSize;
|
||||
options.origin = this.origin;
|
||||
|
||||
let options = this.addOptions(shader.getOptions(node), node);
|
||||
shader.render(this.gl, options);
|
||||
}
|
||||
|
||||
protected addOptions(options: Record<string, any>, node: CanvasNode): Record<string, any> {
|
||||
// Give the shader access to the world size
|
||||
options.worldSize = this.worldSize;
|
||||
|
||||
// Adjust the origin position to the parallax
|
||||
let layer = node.getLayer();
|
||||
let parallax = new Vec2(1, 1);
|
||||
if(layer instanceof ParallaxLayer){
|
||||
parallax = (<ParallaxLayer>layer).parallax;
|
||||
}
|
||||
|
||||
options.origin = this.origin.clone().mult(parallax);
|
||||
|
||||
return options;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,122 @@
|
|||
import Mat4x4 from "../../../DataTypes/Mat4x4";
|
||||
import Vec2 from "../../../DataTypes/Vec2";
|
||||
import Debug from "../../../Debug/Debug";
|
||||
import Rect from "../../../Nodes/Graphics/Rect";
|
||||
import Label from "../../../Nodes/UIElements/Label";
|
||||
import ResourceManager from "../../../ResourceManager/ResourceManager";
|
||||
import QuadShaderType from "./QuadShaderType";
|
||||
|
||||
export default class LabelShaderType extends QuadShaderType {
|
||||
|
||||
constructor(programKey: string){
|
||||
super(programKey);
|
||||
this.resourceManager = ResourceManager.getInstance();
|
||||
}
|
||||
|
||||
initBufferObject(): void {
|
||||
this.bufferObjectKey = "label";
|
||||
this.resourceManager.createBuffer(this.bufferObjectKey);
|
||||
}
|
||||
|
||||
render(gl: WebGLRenderingContext, options: Record<string, any>): void {
|
||||
const backgroundColor = options.backgroundColor.toWebGL();
|
||||
const borderColor = options.borderColor.toWebGL();
|
||||
|
||||
const program = this.resourceManager.getShaderProgram(this.programKey);
|
||||
const buffer = this.resourceManager.getBuffer(this.bufferObjectKey);
|
||||
|
||||
gl.useProgram(program);
|
||||
|
||||
const vertexData = this.getVertices(options.size.x, options.size.y);
|
||||
|
||||
const FSIZE = vertexData.BYTES_PER_ELEMENT;
|
||||
|
||||
// Bind the buffer
|
||||
gl.bindBuffer(gl.ARRAY_BUFFER, buffer);
|
||||
gl.bufferData(gl.ARRAY_BUFFER, vertexData, gl.STATIC_DRAW);
|
||||
|
||||
// Attributes
|
||||
const a_Position = gl.getAttribLocation(program, "a_Position");
|
||||
gl.vertexAttribPointer(a_Position, 2, gl.FLOAT, false, 2 * FSIZE, 0 * FSIZE);
|
||||
gl.enableVertexAttribArray(a_Position);
|
||||
|
||||
// Uniforms
|
||||
const u_BackgroundColor = gl.getUniformLocation(program, "u_BackgroundColor");
|
||||
gl.uniform4fv(u_BackgroundColor, backgroundColor);
|
||||
|
||||
const u_BorderColor = gl.getUniformLocation(program, "u_BorderColor");
|
||||
gl.uniform4fv(u_BorderColor, borderColor);
|
||||
|
||||
const u_MaxSize = gl.getUniformLocation(program, "u_MaxSize");
|
||||
gl.uniform2f(u_MaxSize, -vertexData[0], vertexData[1]);
|
||||
|
||||
// Get transformation matrix
|
||||
// We want a square for our rendering space, so get the maximum dimension of our quad
|
||||
let maxDimension = Math.max(options.size.x, options.size.y);
|
||||
|
||||
const u_BorderWidth = gl.getUniformLocation(program, "u_BorderWidth");
|
||||
gl.uniform1f(u_BorderWidth, options.borderWidth/maxDimension);
|
||||
|
||||
const u_BorderRadius = gl.getUniformLocation(program, "u_BorderRadius");
|
||||
gl.uniform1f(u_BorderRadius, options.borderRadius/maxDimension);
|
||||
|
||||
// The size of the rendering space will be a square with this maximum dimension
|
||||
let size = new Vec2(maxDimension, maxDimension).scale(2/options.worldSize.x, 2/options.worldSize.y);
|
||||
|
||||
// Center our translations around (0, 0)
|
||||
const translateX = (options.position.x - options.origin.x - options.worldSize.x/2)/maxDimension;
|
||||
const translateY = -(options.position.y - options.origin.y - options.worldSize.y/2)/maxDimension;
|
||||
|
||||
// Create our transformation matrix
|
||||
this.translation.translate(new Float32Array([translateX, translateY]));
|
||||
this.scale.scale(size);
|
||||
this.rotation.rotate(options.rotation);
|
||||
let transformation = Mat4x4.MULT(this.translation, this.scale, this.rotation);
|
||||
|
||||
// Pass the translation matrix to our shader
|
||||
const u_Transform = gl.getUniformLocation(program, "u_Transform");
|
||||
gl.uniformMatrix4fv(u_Transform, false, transformation.toArray());
|
||||
|
||||
// Draw the quad
|
||||
gl.drawArrays(gl.TRIANGLE_STRIP, 0, 4);
|
||||
}
|
||||
|
||||
/**
|
||||
* The rendering space always has to be a square, so make sure its square w.r.t to the largest dimension
|
||||
* @param w The width of the quad in pixels
|
||||
* @param h The height of the quad in pixels
|
||||
* @returns An array of the vertices of the quad
|
||||
*/
|
||||
getVertices(w: number, h: number): Float32Array {
|
||||
let x, y;
|
||||
|
||||
if(h > w){
|
||||
y = 0.5;
|
||||
x = w/(2*h);
|
||||
} else {
|
||||
x = 0.5;
|
||||
y = h/(2*w);
|
||||
}
|
||||
|
||||
return new Float32Array([
|
||||
-x, y,
|
||||
-x, -y,
|
||||
x, y,
|
||||
x, -y
|
||||
]);
|
||||
}
|
||||
|
||||
getOptions(rect: Label): Record<string, any> {
|
||||
let options: Record<string, any> = {
|
||||
position: rect.position,
|
||||
backgroundColor: rect.calculateBackgroundColor(),
|
||||
borderColor: rect.calculateBorderColor(),
|
||||
borderWidth: rect.borderWidth,
|
||||
borderRadius: rect.borderRadius,
|
||||
size: rect.size,
|
||||
rotation: rect.rotation
|
||||
}
|
||||
|
||||
return options;
|
||||
}
|
||||
}
|
|
@ -22,24 +22,9 @@ export default class SpriteShaderType extends QuadShaderType {
|
|||
const program = this.resourceManager.getShaderProgram(this.programKey);
|
||||
const buffer = this.resourceManager.getBuffer(this.bufferObjectKey);
|
||||
const texture = this.resourceManager.getTexture(options.imageKey);
|
||||
const image = this.resourceManager.getImage(options.imageKey);
|
||||
|
||||
gl.useProgram(program);
|
||||
|
||||
// Enable texture0
|
||||
gl.activeTexture(gl.TEXTURE0);
|
||||
|
||||
// Bind our texture to texture 0
|
||||
gl.bindTexture(gl.TEXTURE_2D, texture);
|
||||
|
||||
// Set the texture parameters
|
||||
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR);
|
||||
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
|
||||
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
|
||||
|
||||
// Set the texture image
|
||||
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, image);
|
||||
|
||||
const vertexData = this.getVertices(options.size.x, options.size.y, options.scale);
|
||||
|
||||
const FSIZE = vertexData.BYTES_PER_ELEMENT;
|
||||
|
@ -79,9 +64,9 @@ export default class SpriteShaderType extends QuadShaderType {
|
|||
const u_Transform = gl.getUniformLocation(program, "u_Transform");
|
||||
gl.uniformMatrix4fv(u_Transform, false, transformation.toArray());
|
||||
|
||||
// Set texture unit 0 to the sampler
|
||||
// Set up our sampler with our assigned texture unit
|
||||
const u_Sampler = gl.getUniformLocation(program, "u_Sampler");
|
||||
gl.uniform1i(u_Sampler, 0);
|
||||
gl.uniform1i(u_Sampler, texture);
|
||||
|
||||
// Pass in texShift
|
||||
const u_texShift = gl.getUniformLocation(program, "u_texShift");
|
||||
|
|
|
@ -5,7 +5,6 @@ import StringUtils from "../Utils/StringUtils";
|
|||
import AudioManager from "../Sound/AudioManager";
|
||||
import Spritesheet from "../DataTypes/Spritesheet";
|
||||
import WebGLProgramType from "../DataTypes/Rendering/WebGLProgramType";
|
||||
import PhysicsManager from "../Physics/PhysicsManager";
|
||||
|
||||
/**
|
||||
* The resource manager for the game engine.
|
||||
|
@ -80,7 +79,8 @@ export default class ResourceManager {
|
|||
private gl_DefaultShaderPrograms: Map<WebGLProgramType>;
|
||||
private gl_ShaderPrograms: Map<WebGLProgramType>;
|
||||
|
||||
private gl_Textures: Map<WebGLTexture>;
|
||||
private gl_Textures: Map<number>;
|
||||
private gl_NextTextureID: number;
|
||||
private gl_Buffers: Map<WebGLBuffer>;
|
||||
|
||||
private gl: WebGLRenderingContext;
|
||||
|
@ -117,6 +117,7 @@ export default class ResourceManager {
|
|||
this.gl_ShaderPrograms = new Map();
|
||||
|
||||
this.gl_Textures = new Map();
|
||||
this.gl_NextTextureID = 0;
|
||||
this.gl_Buffers = new Map();
|
||||
};
|
||||
|
||||
|
@ -226,7 +227,7 @@ export default class ResourceManager {
|
|||
* @param callback The function to cal when the resources are finished loading
|
||||
*/
|
||||
loadResourcesFromQueue(callback: Function): void {
|
||||
this.loadonly_typesToLoad = 3;
|
||||
this.loadonly_typesToLoad = 5;
|
||||
|
||||
this.loading = true;
|
||||
|
||||
|
@ -443,7 +444,9 @@ export default class ResourceManager {
|
|||
this.images.add(key, image);
|
||||
|
||||
// If WebGL is active, create a texture
|
||||
this.createWebGLTexture(key);
|
||||
if(this.gl_WebGLActive){
|
||||
this.createWebGLTexture(key, image);
|
||||
}
|
||||
|
||||
// Finish image load
|
||||
this.finishLoadingImage(callbackIfLast);
|
||||
|
@ -526,7 +529,7 @@ export default class ResourceManager {
|
|||
|
||||
/* ########## WEBGL SPECIFIC FUNCTIONS ########## */
|
||||
|
||||
public getTexture(key: string): WebGLTexture {
|
||||
public getTexture(key: string): number {
|
||||
return this.gl_Textures.get(key);
|
||||
}
|
||||
|
||||
|
@ -538,10 +541,49 @@ export default class ResourceManager {
|
|||
return this.gl_Buffers.get(key);
|
||||
}
|
||||
|
||||
private createWebGLTexture(key:string): void {
|
||||
if(this.gl_WebGLActive){
|
||||
private createWebGLTexture(imageKey: string, image: HTMLImageElement): void {
|
||||
// Get the texture ID
|
||||
const textureID = this.getTextureID(this.gl_NextTextureID);
|
||||
|
||||
// Create the texture
|
||||
const texture = this.gl.createTexture();
|
||||
this.gl_Textures.add(key, texture);
|
||||
|
||||
// Set up the texture
|
||||
// Enable texture0
|
||||
this.gl.activeTexture(textureID);
|
||||
|
||||
// Bind our texture to texture 0
|
||||
this.gl.bindTexture(this.gl.TEXTURE_2D, texture);
|
||||
|
||||
// Set the texture parameters
|
||||
this.gl.texParameteri(this.gl.TEXTURE_2D, this.gl.TEXTURE_MIN_FILTER, this.gl.LINEAR);
|
||||
this.gl.texParameteri(this.gl.TEXTURE_2D, this.gl.TEXTURE_WRAP_S, this.gl.CLAMP_TO_EDGE);
|
||||
this.gl.texParameteri(this.gl.TEXTURE_2D, this.gl.TEXTURE_WRAP_T, this.gl.CLAMP_TO_EDGE);
|
||||
|
||||
// Set the texture image
|
||||
this.gl.texImage2D(this.gl.TEXTURE_2D, 0, this.gl.RGBA, this.gl.RGBA, this.gl.UNSIGNED_BYTE, image);
|
||||
|
||||
// Add the texture to our map with the same key as the image
|
||||
this.gl_Textures.add(imageKey, this.gl_NextTextureID);
|
||||
|
||||
// Increment the key
|
||||
this.gl_NextTextureID += 1;
|
||||
}
|
||||
|
||||
private getTextureID(id: number): number {
|
||||
// Start with 9 cases - this can be expanded if needed, but for the best performance,
|
||||
// Textures should be stitched into an atlas
|
||||
switch(id){
|
||||
case 0: return this.gl.TEXTURE0;
|
||||
case 1: return this.gl.TEXTURE1;
|
||||
case 2: return this.gl.TEXTURE2;
|
||||
case 3: return this.gl.TEXTURE3;
|
||||
case 4: return this.gl.TEXTURE4;
|
||||
case 5: return this.gl.TEXTURE5;
|
||||
case 6: return this.gl.TEXTURE6;
|
||||
case 7: return this.gl.TEXTURE7;
|
||||
case 8: return this.gl.TEXTURE8;
|
||||
default: return this.gl.TEXTURE9;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -94,7 +94,7 @@ export default class Scene implements Updateable {
|
|||
* @param options The options for Scene initialization
|
||||
*/
|
||||
constructor(viewport: Viewport, sceneManager: SceneManager, renderingManager: RenderingManager, options: Record<string, any>){
|
||||
this.sceneOptions = SceneOptions.parse(options);
|
||||
this.sceneOptions = SceneOptions.parse(options? options : {});
|
||||
|
||||
this.worldSize = new Vec2(500, 500);
|
||||
this.viewport = viewport;
|
||||
|
@ -121,6 +121,9 @@ export default class Scene implements Updateable {
|
|||
this.load = ResourceManager.getInstance();
|
||||
}
|
||||
|
||||
/** A lifecycle method that gets called immediately after a new scene is created, before anything else. */
|
||||
initScene(init: Record<string, any>): void {}
|
||||
|
||||
/** A lifecycle method that gets called when a new scene is created. Load all files you wish to access in the scene here. */
|
||||
loadScene(): void {}
|
||||
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
import Scene from "./Scene";
|
||||
import ResourceManager from "../ResourceManager/ResourceManager";
|
||||
import Viewport from "../SceneGraph/Viewport";
|
||||
import Game from "../Loop/Game";
|
||||
import RenderingManager from "../Rendering/RenderingManager";
|
||||
|
||||
/**
|
||||
|
@ -41,11 +40,14 @@ export default class SceneManager {
|
|||
* Add a scene as the main scene.
|
||||
* Use this method if you've created a subclass of Scene, and you want to add it as the main Scene.
|
||||
* @param constr The constructor of the scene to add
|
||||
* @param init An object to pass to the init function of the new scene
|
||||
*/
|
||||
public addScene<T extends Scene>(constr: new (...args: any) => T, options: Record<string, any>): void {
|
||||
public addScene<T extends Scene>(constr: new (...args: any) => T, init?: Record<string, any>, options?: Record<string, any>): void {
|
||||
let scene = new constr(this.viewport, this, this.renderingManager, options);
|
||||
this.currentScene = scene;
|
||||
|
||||
scene.initScene(init);
|
||||
|
||||
// Enqueue all scene asset loads
|
||||
scene.loadScene();
|
||||
|
||||
|
@ -64,16 +66,12 @@ export default class SceneManager {
|
|||
* Change from the current scene to this new scene.
|
||||
* Use this method if you've created a subclass of Scene, and you want to add it as the main Scene.
|
||||
* @param constr The constructor of the scene to change to
|
||||
* @param init An object to pass to the init function of the new scene
|
||||
*/
|
||||
public changeScene<T extends Scene>(constr: new (...args: any) => T, options: Record<string, any>): void {
|
||||
// unload current scene
|
||||
this.currentScene.unloadScene();
|
||||
public changeScene<T extends Scene>(constr: new (...args: any) => T, init?: Record<string, any>, options?: Record<string, any>): void {
|
||||
this.viewport.setCenter(this.viewport.getHalfSize().x, this.viewport.getHalfSize().y);
|
||||
|
||||
this.resourceManager.unloadAllResources();
|
||||
|
||||
this.viewport.setCenter(0, 0);
|
||||
|
||||
this.addScene(constr, options);
|
||||
this.addScene(constr, init, options);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -88,10 +86,8 @@ export default class SceneManager {
|
|||
* Renders the current Scene
|
||||
*/
|
||||
public render(): void {
|
||||
if(this.currentScene.isRunning()){
|
||||
this.currentScene.render();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the current Scene
|
||||
|
|
|
@ -49,14 +49,10 @@ export default class Viewport {
|
|||
// Set the size of the canvas
|
||||
this.setCanvasSize(canvasSize);
|
||||
|
||||
console.log(canvasSize, zoomLevel);
|
||||
|
||||
// Set the size of the viewport
|
||||
this.setSize(canvasSize);
|
||||
this.setZoomLevel(zoomLevel);
|
||||
|
||||
console.log(this.getHalfSize().toString());
|
||||
|
||||
// Set the center (and make the viewport stay there)
|
||||
this.setCenter(this.view.halfSize.clone());
|
||||
this.setFocus(this.view.halfSize.clone());
|
||||
|
|
|
@ -69,10 +69,10 @@ export default class Color {
|
|||
}
|
||||
|
||||
/**
|
||||
* Purple color
|
||||
* Magenta color
|
||||
* @returns rgb(255, 0, 255)
|
||||
*/
|
||||
static get PURPLE(): Color {
|
||||
static get MAGENTA(): Color {
|
||||
return new Color(255, 0, 255, 1);
|
||||
}
|
||||
|
||||
|
@ -127,7 +127,7 @@ export default class Color {
|
|||
* @returns A new lighter Color
|
||||
*/
|
||||
lighten(): Color {
|
||||
return new Color(MathUtils.clamp(this.r + 40, 0, 255), MathUtils.clamp(this.g + 40, 0, 255), MathUtils.clamp(this.b + 40, 0, 255), this.a);
|
||||
return new Color(MathUtils.clamp(this.r + 40, 0, 255), MathUtils.clamp(this.g + 40, 0, 255), MathUtils.clamp(this.b + 40, 0, 255), MathUtils.clamp(this.a + 10, 0, 255));
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -135,7 +135,7 @@ export default class Color {
|
|||
* @returns A new darker Color
|
||||
*/
|
||||
darken(): Color {
|
||||
return new Color(MathUtils.clamp(this.r - 40, 0, 255), MathUtils.clamp(this.g - 40, 0, 255), MathUtils.clamp(this.b - 40, 0, 255), this.a);
|
||||
return new Color(MathUtils.clamp(this.r - 40, 0, 255), MathUtils.clamp(this.g - 40, 0, 255), MathUtils.clamp(this.b - 40, 0, 255), MathUtils.clamp(this.a + 10, 0, 255));
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
import MathUtils from "./MathUtils";
|
||||
import Color from "./Color";
|
||||
import Perlin from "./Rand/Perlin";
|
||||
import Vec2 from "../DataTypes/Vec2";
|
||||
|
||||
class Noise {
|
||||
p: Perlin = new Perlin();
|
||||
|
@ -22,6 +23,16 @@ export default class RandUtils {
|
|||
return Math.floor(Math.random()*(max - min) + min);
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates a random float in the specified range
|
||||
* @param min The min of the range (inclusive)
|
||||
* @param max The max of the range (exclusive)
|
||||
* @returns A random float in the range [min, max)
|
||||
*/
|
||||
static randFloat(min: number, max: number): number {
|
||||
return Math.random()*(max - min) + min;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates a random hexadecimal number in the specified range
|
||||
* @param min The min of the range (inclusive)
|
||||
|
@ -43,6 +54,10 @@ export default class RandUtils {
|
|||
return new Color(r, g, b);
|
||||
}
|
||||
|
||||
static randVec(minX: number, maxX: number, minY: number, maxY: number): Vec2 {
|
||||
return new Vec2(this.randFloat(minX, maxX), this.randFloat(minY, maxY));
|
||||
}
|
||||
|
||||
/** A noise generator */
|
||||
static noise: Noise = new Noise();
|
||||
|
||||
|
|
|
@ -31,7 +31,7 @@ export default class default_scene extends Scene {
|
|||
// The first argument is the key of the sprite (you get to decide what it is).
|
||||
// The second argument is the path to the actual image.
|
||||
// Paths start in the "dist/" folder, so start building your path from there
|
||||
this.load.image("logo", "demo_assets/wolfie2d_text.png");
|
||||
this.load.image("logo", "demo_assets/images/wolfie2d_text.png");
|
||||
}
|
||||
|
||||
// startScene() is where you should build any game objects you wish to have in your scene,
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import PlayerController from "./PlatformerPlayerController";
|
||||
import Vec2 from "./Wolfie2D/DataTypes/Vec2";
|
||||
import AnimatedSprite from "./Wolfie2D/Nodes/Sprites/AnimatedSprite";
|
||||
import Scene from "./Wolfie2D/Scene/Scene";
|
||||
import Vec2 from "../Wolfie2D/DataTypes/Vec2";
|
||||
import AnimatedSprite from "../Wolfie2D/Nodes/Sprites/AnimatedSprite";
|
||||
import Scene from "../Wolfie2D/Scene/Scene";
|
||||
|
||||
export default class Platformer extends Scene {
|
||||
private player: AnimatedSprite;
|
|
@ -1,9 +1,9 @@
|
|||
import AI from "./Wolfie2D/DataTypes/Interfaces/AI";
|
||||
import Emitter from "./Wolfie2D/Events/Emitter";
|
||||
import GameEvent from "./Wolfie2D/Events/GameEvent";
|
||||
import { GameEventType } from "./Wolfie2D/Events/GameEventType";
|
||||
import Input from "./Wolfie2D/Input/Input";
|
||||
import AnimatedSprite from "./Wolfie2D/Nodes/Sprites/AnimatedSprite";
|
||||
import AI from "../Wolfie2D/DataTypes/Interfaces/AI";
|
||||
import Emitter from "../Wolfie2D/Events/Emitter";
|
||||
import GameEvent from "../Wolfie2D/Events/GameEvent";
|
||||
import { GameEventType } from "../Wolfie2D/Events/GameEventType";
|
||||
import Input from "../Wolfie2D/Input/Input";
|
||||
import AnimatedSprite from "../Wolfie2D/Nodes/Sprites/AnimatedSprite";
|
||||
|
||||
export default class PlayerController implements AI {
|
||||
protected owner: AnimatedSprite;
|
||||
|
@ -16,6 +16,8 @@ export default class PlayerController implements AI {
|
|||
this.emitter = new Emitter();
|
||||
}
|
||||
|
||||
activate(options: Record<string, any>): void {}
|
||||
|
||||
handleEvent(event: GameEvent): void {
|
||||
// Do nothing for now
|
||||
}
|
|
@ -1,68 +0,0 @@
|
|||
import Map from "../Wolfie2D/DataTypes/Map";
|
||||
import Mat4x4 from "../Wolfie2D/DataTypes/Mat4x4";
|
||||
import Vec2 from "../Wolfie2D/DataTypes/Vec2";
|
||||
import RectShaderType from "../Wolfie2D/Rendering/WebGLRendering/ShaderTypes/RectShaderType";
|
||||
|
||||
/**
|
||||
* The gradient circle is technically rendered on a quad, and is similar to a rect, so we'll extend the RectShaderType
|
||||
*/
|
||||
export default class GradientCircleShaderType extends RectShaderType {
|
||||
|
||||
initBufferObject(): void {
|
||||
this.bufferObjectKey = "gradient_circle";
|
||||
this.resourceManager.createBuffer(this.bufferObjectKey);
|
||||
}
|
||||
|
||||
render(gl: WebGLRenderingContext, options: Record<string, any>): void {
|
||||
// Get our program and buffer object
|
||||
const program = this.resourceManager.getShaderProgram(this.programKey);
|
||||
const buffer = this.resourceManager.getBuffer(this.bufferObjectKey);
|
||||
|
||||
// Let WebGL know we're using our shader program
|
||||
gl.useProgram(program);
|
||||
|
||||
// Get our vertex data
|
||||
const vertexData = this.getVertices(options.size.x, options.size.y);
|
||||
const FSIZE = vertexData.BYTES_PER_ELEMENT;
|
||||
|
||||
// Bind the buffer
|
||||
gl.bindBuffer(gl.ARRAY_BUFFER, buffer);
|
||||
gl.bufferData(gl.ARRAY_BUFFER, vertexData, gl.STATIC_DRAW);
|
||||
|
||||
/* ##### ATTRIBUTES ##### */
|
||||
// No texture, the only thing we care about is vertex position
|
||||
const a_Position = gl.getAttribLocation(program, "a_Position");
|
||||
gl.vertexAttribPointer(a_Position, 2, gl.FLOAT, false, 2 * FSIZE, 0 * FSIZE);
|
||||
gl.enableVertexAttribArray(a_Position);
|
||||
|
||||
/* ##### UNIFORMS ##### */
|
||||
// Send the color to the gradient circle
|
||||
const color = options.color.toWebGL();
|
||||
const u_Color = gl.getUniformLocation(program, "u_Color");
|
||||
gl.uniform4fv(u_Color, color);
|
||||
|
||||
// Get transformation matrix
|
||||
// We have a square for our rendering space, so get the maximum dimension of our quad
|
||||
let maxDimension = Math.max(options.size.x, options.size.y);
|
||||
|
||||
// The size of the rendering space will be a square with this maximum dimension
|
||||
let size = new Vec2(maxDimension, maxDimension).scale(2/options.worldSize.x, 2/options.worldSize.y);
|
||||
|
||||
// Center our translations around (0, 0)
|
||||
const translateX = (options.position.x - options.origin.x - options.worldSize.x/2)/maxDimension;
|
||||
const translateY = -(options.position.y - options.origin.y - options.worldSize.y/2)/maxDimension;
|
||||
|
||||
// Create our transformation matrix
|
||||
this.translation.translate(new Float32Array([translateX, translateY]));
|
||||
this.scale.scale(size);
|
||||
this.rotation.rotate(options.rotation);
|
||||
let transformation = Mat4x4.MULT(this.translation, this.scale, this.rotation);
|
||||
|
||||
// Pass the translation matrix to our shader
|
||||
const u_Transform = gl.getUniformLocation(program, "u_Transform");
|
||||
gl.uniformMatrix4fv(u_Transform, false, transformation.toArray());
|
||||
|
||||
// Draw the quad
|
||||
gl.drawArrays(gl.TRIANGLE_STRIP, 0, 4);
|
||||
}
|
||||
}
|
|
@ -1,8 +0,0 @@
|
|||
export enum Homework1Event {
|
||||
PLAYER_DAMAGE = "PLAYER_DAMAGE",
|
||||
SPAWN_FLEET = "SPAWN_FLEET"
|
||||
}
|
||||
|
||||
export enum Homework1Shaders {
|
||||
GRADIENT_CIRCLE = "GRADIENT_CIRCLE"
|
||||
}
|
|
@ -1,107 +0,0 @@
|
|||
import AABB from "../Wolfie2D/DataTypes/Shapes/AABB";
|
||||
import Vec2 from "../Wolfie2D/DataTypes/Vec2";
|
||||
import Graphic from "../Wolfie2D/Nodes/Graphic";
|
||||
import { GraphicType } from "../Wolfie2D/Nodes/Graphics/GraphicTypes";
|
||||
import AnimatedSprite from "../Wolfie2D/Nodes/Sprites/AnimatedSprite";
|
||||
import Scene from "../Wolfie2D/Scene/Scene";
|
||||
import { Homework1Event, Homework1Shaders } from "./HW1_Enums";
|
||||
import SpaceshipPlayerController from "./SpaceshipPlayerController";
|
||||
|
||||
/**
|
||||
* In Wolfie2D, custom scenes extend the original scene class.
|
||||
* This gives us access to lifecycle methods to control our game.
|
||||
*/
|
||||
export default class Homework1_Scene extends Scene {
|
||||
// Here we define member variables of our game, and object pools for adding in game objects
|
||||
private player: AnimatedSprite;
|
||||
|
||||
// Create an object pool for our fleet
|
||||
private MAX_FLEET_SIZE = 20;
|
||||
private fleet: Array<AnimatedSprite> = new Array(this.MAX_FLEET_SIZE);
|
||||
|
||||
// Create an object pool for our fleet
|
||||
private MAX_NUM_ASTEROIDS = 6;
|
||||
private asteroids: Array<Graphic> = new Array(this.MAX_NUM_ASTEROIDS);
|
||||
|
||||
// Create an object pool for our fleet
|
||||
private MAX_NUM_MINERALS = 20;
|
||||
private minerals: Array<Graphic> = new Array(this.MAX_NUM_MINERALS);
|
||||
|
||||
/*
|
||||
* loadScene() overrides the parent class method. It allows us to load in custom assets for
|
||||
* use in our scene.
|
||||
*/
|
||||
loadScene(){
|
||||
/* ##### DO NOT MODIFY ##### */
|
||||
// Load in the player spaceship spritesheet
|
||||
this.load.spritesheet("player", "hw1_assets/spritesheets/player_spaceship.json");
|
||||
|
||||
/* ##### YOUR CODE GOES BELOW THIS LINE ##### */
|
||||
}
|
||||
|
||||
/*
|
||||
* startScene() allows us to add in the assets we loaded in loadScene() as game objects.
|
||||
* Everything here happens strictly before update
|
||||
*/
|
||||
startScene(){
|
||||
/* ##### DO NOT MODIFY ##### */
|
||||
// Create a layer to serve as our main game - Feel free to use this for your own assets
|
||||
// It is given a depth of 5 to be above our background
|
||||
this.addLayer("primary", 5);
|
||||
|
||||
// Add in the player as an animated sprite
|
||||
// We give it the key specified in our load function and the name of the layer
|
||||
this.player = this.add.animatedSprite("player", "primary");
|
||||
|
||||
// Set the player's position to the middle of the screen, and scale it down
|
||||
this.player.position.set(this.viewport.getCenter().x, this.viewport.getCenter().y);
|
||||
this.player.scale.set(0.5, 0.5);
|
||||
|
||||
// Play the idle animation by default
|
||||
this.player.animation.play("idle");
|
||||
|
||||
// Add physics to the player
|
||||
let playerCollider = new AABB(Vec2.ZERO, new Vec2(64, 64));
|
||||
|
||||
// We'll specify a smaller collider centered on the player.
|
||||
// Also, we don't need collision handling, so disable it.
|
||||
this.player.addPhysics(playerCollider, Vec2.ZERO, false);
|
||||
|
||||
// Add a a playerController to the player
|
||||
this.player.addAI(SpaceshipPlayerController, {owner: this.player, spawnFleetEventKey: "spawnFleet"});
|
||||
|
||||
/* ##### YOUR CODE GOES BELOW THIS LINE ##### */
|
||||
// Initialize the fleet object pool
|
||||
|
||||
// Initialize the mineral object pool
|
||||
for(let i = 0; i < this.minerals.length; i++){
|
||||
this.minerals[i] = this.add.graphic(GraphicType.RECT, "primary", {position: new Vec2(0, 0), size: new Vec2(32, 32)});
|
||||
this.minerals[i].visible = false;
|
||||
}
|
||||
|
||||
// Initialize the asteroid object pool
|
||||
let gc = this.add.graphic(GraphicType.RECT, "primary", {position: new Vec2(400, 400), size: new Vec2(100, 100)});
|
||||
gc.useCustomShader(Homework1Shaders.GRADIENT_CIRCLE);
|
||||
|
||||
// Subscribe to events
|
||||
this.receiver.subscribe(Homework1Event.PLAYER_DAMAGE);
|
||||
this.receiver.subscribe(Homework1Event.SPAWN_FLEET);
|
||||
}
|
||||
|
||||
/*
|
||||
* updateScene() is where the real work is done. This is where any custom behavior goes.
|
||||
*/
|
||||
updateScene(){
|
||||
// Handle events we care about
|
||||
while(this.receiver.hasNextEvent()){
|
||||
let event = this.receiver.getNextEvent();
|
||||
}
|
||||
|
||||
// Check for collisions
|
||||
for(let i = 0; i < this.minerals.length; i++){
|
||||
if(this.player.collisionShape.overlaps(this.minerals[i].boundary)){
|
||||
console.log(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,77 +0,0 @@
|
|||
import AI from "../Wolfie2D/DataTypes/Interfaces/AI";
|
||||
import Vec2 from "../Wolfie2D/DataTypes/Vec2";
|
||||
import Debug from "../Wolfie2D/Debug/Debug";
|
||||
import Emitter from "../Wolfie2D/Events/Emitter";
|
||||
import GameEvent from "../Wolfie2D/Events/GameEvent";
|
||||
import Input from "../Wolfie2D/Input/Input";
|
||||
import AnimatedSprite from "../Wolfie2D/Nodes/Sprites/AnimatedSprite";
|
||||
import MathUtils from "../Wolfie2D/Utils/MathUtils";
|
||||
import { Homework1Event } from "./HW1_Enums";
|
||||
|
||||
export default class SpaceshipPlayerController implements AI {
|
||||
// We want to be able to control our owner, so keep track of them
|
||||
private owner: AnimatedSprite;
|
||||
|
||||
// The direction the spaceship is moving
|
||||
private direction: Vec2;
|
||||
private MIN_SPEED: number = 0;
|
||||
private MAX_SPEED: number = 300;
|
||||
private speed: number;
|
||||
private ACCELERATION: number = 4;
|
||||
private rotationSpeed: number;
|
||||
|
||||
// An emitter to hook into the event queue
|
||||
private emitter: Emitter;
|
||||
|
||||
initializeAI(owner: AnimatedSprite, options: Record<string, any>): void {
|
||||
this.owner = owner;
|
||||
|
||||
// Start facing up
|
||||
this.direction = new Vec2(0, 1);
|
||||
this.speed = 0;
|
||||
this.rotationSpeed = 2;
|
||||
|
||||
this.emitter = new Emitter();
|
||||
}
|
||||
|
||||
handleEvent(event: GameEvent): void {
|
||||
// We need to handle animations when we get hurt
|
||||
if(event.type === Homework1Event.PLAYER_DAMAGE){
|
||||
this.owner.animation.play("shield");
|
||||
}
|
||||
}
|
||||
|
||||
update(deltaT: number): void {
|
||||
// We need to handle player input
|
||||
let forwardAxis = (Input.isPressed('forward') ? 1 : 0) + (Input.isPressed('backward') ? -1 : 0);
|
||||
let turnDirection = (Input.isPressed('turn_ccw') ? -1 : 0) + (Input.isPressed('turn_cw') ? 1 : 0);
|
||||
|
||||
// Space controls - speed stays the same if nothing happens
|
||||
// Forward to speed up, backward to slow down
|
||||
this.speed += this.ACCELERATION * forwardAxis;
|
||||
this.speed = MathUtils.clamp(this.speed, this.MIN_SPEED, this.MAX_SPEED);
|
||||
|
||||
// Rotate the player
|
||||
this.direction.rotateCCW(turnDirection * this.rotationSpeed * deltaT);
|
||||
|
||||
// Update the visual direction of the player
|
||||
this.owner.rotation = -(Math.atan2(this.direction.y, this.direction.x) - Math.PI/2);
|
||||
|
||||
// Move the player with physics
|
||||
this.owner.move(this.direction.scaled(-this.speed * deltaT));
|
||||
|
||||
// If the player clicked, we need to spawn in a fleet member
|
||||
if(Input.isMouseJustPressed()){
|
||||
this.emitter.fireEvent(Homework1Event.SPAWN_FLEET, {position: Input.getGlobalMousePosition()});
|
||||
}
|
||||
|
||||
// Animations
|
||||
if(!this.owner.animation.isPlaying("shield")){
|
||||
if(this.speed > 0){
|
||||
this.owner.animation.playIfNotAlready("boost");
|
||||
} else {
|
||||
this.owner.animation.playIfNotAlready("idle");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -22,6 +22,15 @@
|
|||
left: 0px;
|
||||
}
|
||||
|
||||
#text-canvas {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
position: absolute;
|
||||
top: 0px;
|
||||
left: 0px;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
#debug-canvas {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
|
@ -38,6 +47,8 @@
|
|||
<div id="game-window">
|
||||
<!-- This is the canvas where the actual game is rendered -->
|
||||
<canvas id="game-canvas"></canvas>
|
||||
<!-- This is the canvas where text is rendered in WebGL -->
|
||||
<canvas id="text-canvas" hidden></canvas>
|
||||
<!-- This is the canvas where the debug text and graphics are rendered -->
|
||||
<canvas id="debug-canvas"></canvas>
|
||||
</div>
|
||||
|
|
38
src/main.ts
38
src/main.ts
|
@ -1,36 +1,24 @@
|
|||
import Game from "./Wolfie2D/Loop/Game";
|
||||
import Homework1_Scene from "./hw1/HW1_Scene";
|
||||
import Registry from "./Wolfie2D/Registry/Registry";
|
||||
import { Homework1Shaders } from "./hw1/HW1_Enums";
|
||||
import GradientCircleShaderType from "./hw1/GradientCircleShaderType";
|
||||
import default_scene from "./default_scene";
|
||||
|
||||
// The main function is your entrypoint into Wolfie2D. Specify your first scene and any options here.
|
||||
(function main(){
|
||||
// Set up options
|
||||
let options = {
|
||||
canvasSize: {x: 1200, y: 800},
|
||||
clearColor: {r: 0.1, g: 0.1, b: 0.1},
|
||||
inputs: [
|
||||
{ name: "forward", keys: ["w"] },
|
||||
{ name: "backward", keys: ["s"] },
|
||||
{ name: "turn_ccw", keys: ["a"] },
|
||||
{ name: "turn_cw", keys: ["d"] },
|
||||
],
|
||||
useWebGL: true,
|
||||
showDebug: false
|
||||
}
|
||||
// Run any tests
|
||||
runTests();
|
||||
|
||||
// We have a custom shader, so lets add it to the registry and preload it
|
||||
Registry.shaders.registerAndPreloadItem(
|
||||
Homework1Shaders.GRADIENT_CIRCLE, // The key of the shader program
|
||||
GradientCircleShaderType, // The constructor of the shader program
|
||||
"hw1_assets/shaders/gradient_circle.vshader", // The path to the vertex shader
|
||||
"hw1_assets/shaders/gradient_circle.fshader"); // the path to the fragment shader
|
||||
// Set up options for our game
|
||||
let options = {
|
||||
canvasSize: {x: 1200, y: 800}, // The size of the game
|
||||
clearColor: {r: 0.1, g: 0.1, b: 0.1}, // The color the game clears to
|
||||
useWebGL: true, // Tell the game we want to use webgl
|
||||
showDebug: false // Whether to show debug messages. You can change this to true if you want
|
||||
}
|
||||
|
||||
// Create a game with the options specified
|
||||
const game = new Game(options);
|
||||
|
||||
// Start our game
|
||||
game.start();
|
||||
game.getSceneManager().addScene(Homework1_Scene, {});
|
||||
game.start(default_scene, {});
|
||||
})();
|
||||
|
||||
function runTests(){};
|
Loading…
Reference in New Issue
Block a user