Skip to content

Instantly share code, notes, and snippets.

@reZach
Created June 18, 2019 02:08
Show Gist options
  • Save reZach/15da63f816e0d20fe8446be30f30f1b5 to your computer and use it in GitHub Desktop.
Save reZach/15da63f816e0d20fe8446be30f30f1b5 to your computer and use it in GitHub Desktop.
Animates Terrain in Game Builder (Steam)
// Example card.
// User-editable properties for this card:
export const PROPS = [
propNumber("ticks", 60),
propBoolean("infinite", true),
propBoolean("completeAnimationDelay", false),
propNumber("completeAnimationDelayMin", 60),
propNumber("completeAnimationDelayMax", 60),
propNumber("forceFrame", -1)
];
/**
* Sets the block relative to the pos.
* @param {THREE.Vector3} pos The relative position (typically an actor)
* @param {int} xOffset The x-offset relative to pos where you'd like to place the block
* @param {int} yOffset The y-offset relative to pos where you'd like to place the block
* @param {int} zOffset The z-offset relative to pos where you'd like to place the block
* @param {BlockShape} blockShape The shape of the block
* @param {BlockDir} blockDir The direction of the block
* @param {BlockStyle} blockStyle The style of the block
*/
var sbr = function (pos, xOffset, yOffset, zOffset,
blockShape, blockDir, blockStyle) {
// Blocks extend 2.5 units on the x-plane,
// and 1.5 units in the y-plane
var newX = (pos.x + (xOffset * 2.5)) / 2.5;
var newY = (pos.y + (yOffset * 1.5)) / 1.5;
var newZ = (pos.z + (zOffset * 2.5)) / 2.5;
setBlock(
newX,
newY,
newZ,
blockShape,
blockDir,
blockStyle
);
};
/**
* Clears the block relative to the pos.
* @param {THREE.Vector3} pos The relative position (typically an actor)
* @param {int} xOffset The x-offset relative to pos where you'd like to clear the block
* @param {int} yOffset The y-offset relative to pos where you'd like to clear the block
* @param {int} zOffset The z-offset relative to pos where you'd like to clear the block
*/
var cbr = function (pos, xOffset, yOffset, zOffset) {
// Blocks extend 2.5 units on the x-plane,
// and 1.5 units in the y-plane
var newX = (pos.x + (xOffset * 2.5)) / 2.5;
var newY = (pos.y + (yOffset * 1.5)) / 1.5;
var newZ = (pos.z + (zOffset * 2.5)) / 2.5;
clearBlock(
newX,
newY,
newZ
);
};
var Animation = (function (origin) {
var _tick = 0; // Holds the count of ticks
var _frames = []; // All of our frames for our animation
var _currentFrame = 0; // The current frame we are showing
var _drawnFirstFrame = false; // If we've drawn the first frame yet
var _createdCoordinates = false;
var _animationCompleteDelayActive = false;
var _animationCompleteDelayTimer = 0;
var _ended = false; // If the animation should be ended
var _origin = origin; // Holds the origin (position (x,y,z)) of this animation
var _savedBlocks = {}; // Holds saved blocks used in your animation - to save typing!
var createCoordinates = function () {
if (_createdCoordinates) {
return;
}
for (var i = 0; i < _frames.length; i++) {
for (var j = 0; j < _frames[i].length; j++) {
if (typeof _frames[i][j].x === "undefined" ||
typeof _frames[i][j].y === "undefined" ||
typeof _frames[i][j].z === "undefined") {
_frames[i][j].x = _frames[i][j].xOffset;
_frames[i][j].y = _frames[i][j].yOffset;
_frames[i][j].z = _frames[i][j].zOffset;
}
}
}
_createdCoordinates = true;
}
/*
* Clears out all frames.
*/
var clearAllFrames = function () {
for (var i = 0; i < _frames.length; i++) {
for (var j = 0; j < _frames[i].length; j++) {
cbr(_origin, _frames[i][j].x, _frames[i][j].y, _frames[i][j].z);
}
}
};
/**
* The main drawing function.
* @param {int} frameNum The frame to display
*/
var draw = function (frameNum) {
createCoordinates();
clearAllFrames();
// Draw new blocks
for (var i = 0; i < _frames[frameNum].length; i++) {
// Load from saved blocks
if (typeof _frames[frameNum][i].block !== "undefined") {
var block = _savedBlocks[_frames[frameNum][i].block];
sbr(_origin, _frames[frameNum][i].x, _frames[frameNum][i].y, _frames[frameNum][i].z,
block.shape, block.dir, block.style);
} else {
sbr(_origin, _frames[frameNum][i].x, _frames[frameNum][i].y, _frames[frameNum][i].z,
_frames[frameNum][i].shape, _frames[frameNum][i].dir, _frames[frameNum][i].style);
}
}
};
return {
/**
* Sets the origin (x,y,z) of where this animation should originate from.
* @param {THREE.Vector3} origin
*/
setOrigin: function (origin) {
_origin = origin;
},
/**
* Saves block data so that you can reference the data by a name,
* instead of typing out the block data for each identical block.
* @param {Object} block Block information that should be saved
* @param {string} name A friendly name of this block
*/
saveBlock: function (block, name) {
if (typeof _savedBlocks[name] === "undefined") {
_savedBlocks[name] = block;
}
},
/**
* To be called from the "onTick" function.
*/
play: function () {
// If the animation is one-time,
// don't attempt to run it again
if (_ended && !props.infinite) {
return;
}
// Force a particular frame, only if it's in the
// range of available frames.
if (props.forceFrame >= 0 && props.forceFrame < _frames.length) {
_currentFrame = props.forceFrame;
draw(props.forceFrame);
return;
}
_tick++;
// Delay the next animation loop based on our props
if (_animationCompleteDelayActive){
if (_tick >= _animationCompleteDelayTimer){
_tick = props.ticks;
_animationCompleteDelayActive = false;
_animationCompleteDelayTimer = 0;
} else {
return;
}
}
if (_tick >= props.ticks) {
_tick = 0;
_currentFrame++;
if (_currentFrame >= _frames.length) {
if (!props.infinite) {
_ended = true;
clearAllFrames();
return;
} else {
_currentFrame = 0;
// Reset the animation lock timer if enabled
if (props.completeAnimationDelay){
_animationCompleteDelayActive = true;
_currentFrame = -1;
if (props.completeAnimationDelayMin < props.completeAnimationDelayMax){
_animationCompleteDelayTimer = props.completeAnimationDelayMax;
} else {
_animationCompleteDelayTimer = Math.floor(
Math.random() * props.completeAnimationDelayMax + props.completeAnimationDelayMin);
}
clearAllFrames();
return;
}
}
}
draw(_currentFrame);
} else {
// Draw the first frame (only once)
if (!_drawnFirstFrame && _currentFrame === 0) {
_drawnFirstFrame = true;
draw(_currentFrame);
}
}
},
/**
* Adds a block, or an array of blocks to be saved
* for a single frame.
* @param {array} blocks An array of blocks for a given frame
*/
addFrame: function (blocks) {
if (!Array.isArray(blocks)) {
_frames.push([blocks]);
} else {
_frames.push(blocks);
}
},
SHAPES: {
PYRAMID: "PYRAMID"
},
create: function (shape, options) {
var blockData = [];
var style = 0;
// Load style from block object,
// or otherwise saved blocks
if (typeof options.block === "object"){
style = options.block.style;
} else if (typeof options.block === "string"){
if (typeof _savedBlocks[options.block] !== "undefined"){
style = _savedBlocks[options.block].style;
}
}
/*
options: {
xOffset: 0,
yOffset: 0,
zOffset: 0,
height: x,
dir: "N",
block: {
shape: BlockShape.BOX,
dir: BlockDir.NORTH,
style: 26
}
}
*/
switch (shape) {
case this.SHAPES.PYRAMID:
if (options.height < 1){
break;
}
// Create "tip"
blockData.push({
xOffset: options.xOffset,
yOffset: options.yOffset,
zOffset: options.zOffset,
shape: BlockShape.CORNER,
dir: BlockDir.WEST,
style: style
});
blockData.push({
xOffset: options.xOffset,
yOffset: options.yOffset,
zOffset: options.zOffset + 1,
shape: BlockShape.CORNER,
dir: BlockDir.SOUTH,
style: style
});
blockData.push({
xOffset: options.xOffset - 1,
yOffset: options.yOffset,
zOffset: options.zOffset + 1,
shape: BlockShape.CORNER,
dir: BlockDir.EAST,
style: style
});
blockData.push({
xOffset: options.xOffset - 1,
yOffset: options.yOffset,
zOffset: options.zOffset,
shape: BlockShape.CORNER,
dir: BlockDir.NORTH,
style: style
});
for (var i = 1; i <= options.height; i++) {
// if (i > 1 && i !== options.height){
// // Create "column" support
// blockData.push({
// xOffset: options.xOffset,
// yOffset: options.yOffset - i,
// zOffset: options.zOffset,
// shape: BlockShape.BOX,
// dir: BlockDir.WEST,
// style: options.block.style
// });
// blockData.push({
// xOffset: options.xOffset,
// yOffset: options.yOffset - i,
// zOffset: options.zOffset + 1,
// shape: BlockShape.BOX,
// dir: BlockDir.SOUTH,
// style: options.block.style
// });
// blockData.push({
// xOffset: options.xOffset - 1,
// yOffset: options.yOffset - i,
// zOffset: options.zOffset + 1,
// shape: BlockShape.BOX,
// dir: BlockDir.EAST,
// style: options.block.style
// });
// blockData.push({
// xOffset: options.xOffset - 1,
// yOffset: options.yOffset - i,
// zOffset: options.zOffset,
// shape: BlockShape.BOX,
// dir: BlockDir.NORTH,
// style: options.block.style
// });
// }
if (i > 1){
// Create pyramid sides
for (var j = 0; j < i * 2; j++){
if (j === 0){
blockData.push({
xOffset: options.xOffset + (i - 1),
yOffset: options.yOffset - (i - 1),
zOffset: options.zOffset - (i - 1) + j,
shape: BlockShape.CORNER,
dir: BlockDir.WEST,
style: style
});
continue;
}
if (j === ((i * 2) - 1)){
blockData.push({
xOffset: options.xOffset + (i - 1),
yOffset: options.yOffset - (i - 1),
zOffset: options.zOffset - (i - 1) + j,
shape: BlockShape.CORNER,
dir: BlockDir.SOUTH,
style: style
});
continue;
}
blockData.push({
xOffset: options.xOffset + (i - 1),
yOffset: options.yOffset - (i - 1),
zOffset: options.zOffset - (i - 1) + j,
shape: BlockShape.RAMP,
dir: BlockDir.WEST,
style: style
});
}
for (var j = 0; j < i * 2; j++){
if (j === 0) {
continue;
}
if (j === ((i * 2) - 1)){
blockData.push({
xOffset: options.xOffset + (i - 1) - j,
yOffset: options.yOffset - (i - 1),
zOffset: options.zOffset - (i - 1),
shape: BlockShape.CORNER,
dir: BlockDir.NORTH,
style: style
});
continue;
}
blockData.push({
xOffset: options.xOffset + (i - 1) - j,
yOffset: options.yOffset - (i - 1),
zOffset: options.zOffset - (i - 1),
shape: BlockShape.RAMP,
dir: BlockDir.NORTH,
style: style
});
}
for (var j = 0; j < i * 2; j++){
if (j === 0){
continue;
}
if (j === ((i * 2) - 1)){
blockData.push({
xOffset: options.xOffset + (i - 1) - j,
yOffset: options.yOffset - (i - 1),
zOffset: options.zOffset + i,
shape: BlockShape.CORNER,
dir: BlockDir.EAST,
style: style
});
continue;
}
blockData.push({
xOffset: options.xOffset + (i - 1) - j,
yOffset: options.yOffset - (i - 1),
zOffset: options.zOffset + i,
shape: BlockShape.RAMP,
dir: BlockDir.SOUTH,
style: style
});
}
for (var j = 0; j < i * 2; j++){
if (j === 0 || j === ((i * 2) - 1)){
continue;
}
blockData.push({
xOffset: options.xOffset - i,
yOffset: options.yOffset - (i - 1),
zOffset: options.zOffset - (i - 1) + j,
shape: BlockShape.RAMP,
dir: BlockDir.EAST,
style: style
});
}
}
}
break;
default:
break;
}
return blockData;
}
}
})();
// Save blocks that we want to use later
Animation.saveBlock({
shape: BlockShape.BOX,
dir: BlockDir.NORTH,
style: 26 // ocean, doesn't work atm
}, "ocean");
// Save animation frame data
Animation.addFrame(
Animation.create(Animation.SHAPES.PYRAMID, {
xOffset: 0,
yOffset: 0,
zOffset: 0,
height: 1,
block: "ocean"
})
);
Animation.addFrame(
Animation.create(Animation.SHAPES.PYRAMID, {
xOffset: 1,
yOffset: 1,
zOffset: 0,
height: 2,
block: {
shape: BlockShape.BOX,
dir: BlockDir.NORTH,
style: 26
}
})
);
Animation.addFrame(
Animation.create(Animation.SHAPES.PYRAMID, {
xOffset: 2,
yOffset: 0,
zOffset: 0,
height: 1,
block: {
shape: BlockShape.BOX,
dir: BlockDir.NORTH,
style: 26
}
})
);
Animation.addFrame(
Animation.create(Animation.SHAPES.PYRAMID, {
xOffset: 3,
yOffset: 1,
zOffset: 0,
height: 2,
block: {
shape: BlockShape.BOX,
dir: BlockDir.NORTH,
style: 26
}
})
);
Animation.addFrame(
Animation.create(Animation.SHAPES.PYRAMID, {
xOffset: 4,
yOffset: 0,
zOffset: 0,
height: 1,
block: {
shape: BlockShape.BOX,
dir: BlockDir.NORTH,
style: 26
}
})
);
// Animation.addFrame([{
// x: 0,
// y: 0,
// z: 0,
// block: "ocean"
// }]);
// Animation.addFrame([{
// x: 0,
// y: 0,
// z: 5,
// block: "ocean"
// }, {
// x: 0,
// y: 0,
// z: 4,
// block: "ocean"
// }]);
// Animation.addFrame([{
// x: 0,
// y: 0,
// z: 4,
// block: "ocean"
// }, {
// x: 0,
// y: 0,
// z: 3,
// block: "ocean"
// }, {
// x: -1,
// y: 0,
// z: 3,
// block: "ocean"
// }, {
// x: 1,
// y: 0,
// z: 3,
// block: "ocean"
// }, {
// x: 0,
// y: 1,
// z: 3,
// block: "ocean"
// }, {
// x: 0,
// y: 0,
// z: 2,
// block: "ocean"
// }]);
// Animation.addFrame([{
// x: 0,
// y: 0,
// z: 2,
// block: "ocean"
// }, {
// x: 0,
// y: 0,
// z: 1,
// block: "ocean"
// }]);
// Animation.addFrame([{
// x: 0,
// y: 0,
// z: 0,
// block: "ocean"
// }]);
// onTick is called every frame (50-60 times per second).
export function onTick() {
Animation.setOrigin(getPos());
Animation.play();
}
export function onCollision(msg) {
cooldown(1);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment