p5.raycaster
Version:
a simple p5js library for semi 3d rendering with ray casting
224 lines (207 loc) • 7.38 kB
JavaScript
class Sprite {
/**
* init a sprite
* @param {p5.Image} source source image for the sprite, single image is load and use for animation and rotation (if any), see readme for more info
* @param {Vector} pos {x, y} or a p5.Vector object
* @param {number} width
* @param {number} height
* @param {number} [angle = 0]
* @param {number} [yAdjustment=0] [-0.5, 0.5] -0.5 ~ 0.5, negative number will make the sprite appear lower and positive number will make it appear higher
* @param {number} [animationGap=0] number of frame for before change to the next animation
* @param {p5} [p5Inst = null] reference to the p5 instance if in instance mode, if not specified try to call functions blind to windows
*/
constructor(source, pos, width, height, angle = 0, yAdjustment = 0, animationGap = 0, p5Inst = null) {
this.pos = { x: pos.x, y: pos.y };
this.ang = angle;
this.src = source;
this.width = width;
this.height = height;
this.pInst = p5Inst;
this.yAdjustment = yAdjustment;
this.animationGap = animationGap;
this.scaleP = {x: 1, y: 1};
//
this.buffer = null;
if (this.pInst !== null) {
this.buffer = this.pInst.createGraphics(this.width, this.height);
} else {
if (window.createGraphics) {
this.buffer = window.createGraphics(this.width, this.height);
} else {
throw new Error("p5 is not found!")
}
}
//
this.animationFrames = Math.floor(this.src.height / this.height);
this.animationGroups = [];
let t = [];
for (let i = 0; i < this.animationFrames; i++) {
t.push(i);
}
this.animationGroups.push(t);
this.rotationFrames = Math.floor(this.src.width / this.width);
this.currentRotation = 0;
this.currentAnimation = 0;
this.currentAnimationGroup = 0;
this.currentAnimationIdx = 0;
if (this.rotationFrames > 1) {
this.rotationDivision = (Math.PI * 2) / this.rotationFrames;
this.updateRotationFrame(Math.PI);
}
this.drawBuffer();
}
/**
*
* @param {number} gap number of frame (main canvas) for before change to the other animation
*/
setAnimationGap(gap) {
this.animationGap = gap;
}
/**
* advance animation control
* @param {Array} grouping arrays of animation frames in group, e.g. [[0,1,2,3],[4,5,6]]
*/
setAnimationGroups(grouping) {
this.animationGroups = grouping;
}
/**
*
* @param {number} group
*/
setCurrentAnimationGroup(group) {
if (this.animationGroups && group > -1 && group < this.animationGroups.length) {
this.currentAnimationGroup = group;
this.currentAnimationIdx = 0;
this.currentAnimation = this.animationGroups[this.currentAnimationGroup][this.currentAnimationIdx];
}
}
/**
*
* @param {number} ratio -0.5 ~ 0.5, negative number will make the sprite appear lower and positive number will make it appear higher
*/
setYAdjustment(ratio) {
this.yAdjustment = ratio;
}
/**
* update the animation, should have animation rate set
*
* @param {number} frameCount current frame count from main canvas
*/
update(frameCount) {
if (this.animationGap <= 0 || this.animationFrames === 1) return;
if (frameCount % this.animationGap === 0) this.nextAnimationFrame();
}
nextAnimationFrame() {
this.currentAnimationIdx++;
this.currentAnimationIdx = this.currentAnimationIdx % this.animationGroups[this.currentAnimationGroup].length;
this.currentAnimation = this.animationGroups[this.currentAnimationGroup][this.currentAnimationIdx];
this.drawBuffer();
}
/**
* update the new frame for the animation
* @param {number} newFrame
*/
updateAnimationFrame(newFrame) {
if (this.currentAnimation !== newFrame) {
this.currentAnimation = newFrame;
let g, i;
this.animationGroups.forEach((group, id) => {
let ii = group.indexOf(this.currentAnimation);
if (ii > -1) {
g = id;
i = ii;
}
});
this.currentAnimationGroup = g;
this.currentAnimationIdx = i;
this.drawBuffer();
}
}
/**
*
* @param {number} angle
*/
updateRotationFrame(angle) {
let deltaAng, newRotation;
if (this.rotationFrames === 1) {
newRotation = 0;
} else {
deltaAng = angle - this.ang + this.rotationDivision / 2;
while (deltaAng < 0) {
deltaAng += Math.PI * 2;
}
while (deltaAng > Math.PI * 2) {
deltaAng -= Math.PI * 2;
}
newRotation = Math.floor((deltaAng) / this.rotationDivision);
}
if (this.currentRotation !== newRotation) {
this.currentRotation = newRotation;
this.drawBuffer();
}
}
/**
*
* @param {Vector} movement {x, y} or a p5.Vector
*/
move(movement) {
if (!this.world) return;
let idx1 = Math.floor(this.pos.x + movement.x) + Math.floor(this.pos.y) * this.world.width;
let idx2 = Math.floor(this.pos.x) + Math.floor(this.pos.y + movement.y) * this.world.width;
if (this.world.map[idx1] === this.world.table.MAP_FLOOR || this.world.doorStates[idx1] === this.world.table.DOOR_OPEN) {
if (Math.floor(this.pos.x + movement.x) > -1 && Math.floor(this.pos.x + movement.x) < this.world.width) this.pos.x += movement.x; // can't go out sdie the world
}
if (this.world.map[idx2] === this.world.table.MAP_FLOOR || this.world.doorStates[idx2] === this.world.table.DOOR_OPEN) {
if (Math.floor(this.pos.y + movement.y) > -1 && Math.floor(this.pos.y + movement.y) < this.world.height) this.pos.x += movement.y;
}
}
/**
*
* @param {number} angle
*/
rotate(angle) {
this.ang += angle;
}
/**
*
* @param {number} angle
*/
rotateTo(angle){
this.ang = angle
}
/**
*
* @param {number} xRatio
* @param {number} yRatio
*/
scale(xRatio, yRatio){
if (arguments.length === 1) yRatio = xRatio
this.scaleP.x *= xRatio;
this.scaleP.y *= yRatio;
}
/**
* scale to the ratio relative to the origin size
* @param {number} xRatio
* @param {number} yRatio
*/
scaleTo(xRatio, yRatio){
if (arguments.length === 1) yRatio = xRatio
this.scaleP.x = xRatio;
this.scaleP.y = yRatio;
}
/**
* update image buffer of the sprite
*/
drawBuffer() {
this.buffer.clear();
this.buffer.image(this.src, 0, 0, this.width, this.height, this.currentRotation * this.width, this.currentAnimation * this.height, this.width, this.height);
}
/**
* destroy the sprite to free resources
*/
destroy() {
this.drawBuffer.remove();
delete this;
}
}
export default Sprite;