在 p5.js 中,是否可以在设置或预加载函数中加载一个 gif,然后在其他地方多次使用该 gif?
In p5.js, is it possible to load a gif in the setup or preload function, and then use that gif multiple times elsewhere?
我有一个 class(我们称它为“敌人”),我希望它们在离我足够近时攻击我(它会显示动画 gif,看起来像一口咬人)。
除了我能弄清楚的唯一方法是将 loadImage("attack.gif"), 放在 class 中,我已经完成了所有这些工作。这变得非常快,因为每次敌人出现时,它都必须重新加载该 gif。
我尝试在我的 class 中使用从 setup() 加载的 gif,但他们的所有攻击都是同步的。
还有其他方法吗?
您可以预加载 gif(或多个 gif)并将它们存储在一个数组中,稍后您可以在多个地方重新使用该数组(例如,在您的情况下是多个生成的敌人)。
这里有一个基本的例子来演示:
- 正在将图像加载到数组中
- 使用加载的图像在屏幕上(重新)生成对象
let images;
let enemies = [];
function preload(){
// load images and store them in an array
images = [
loadImage("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAIAAAACACAMAAAD04JH5AAAAPFBMVEX///8AAADf399fX19/f38/Pz+/v78fHx+fn58XFxeHh4cPDw+np6fX19fHx8evr6+3t7dvb2/39/dPT08jn1NmAAAA+UlEQVR4nO3ZSRKCMBBAUQnzPHj/u7qwOwtS0QhBwfpviaH9bDTG2w0AAAAAgNNLE2HC7zF6T0oAAQT8Q0CTC1M8Df61gywxek8TIcAq5anKXUsIIICACwaMmWhlept5OUvGCAH2o3iLqN8FBBBAwJcDJt3U1Dqsk+1O538/Z0mtU6aPAypnuu4JjfNKwJKKAAIIuEzAi+lxvPuFSQABBJwnoHY2JFu4U4ID7GYqzpYs+KiLAAIIOHPA7D+aWZsPCdh1Wk4AAQRcJ2CRQ5ai1yu9XrnnK/YfqWb9SuqfsoQ/xpoehSf+x1PHHtcTQAABPwsAAAAAAOAYD7+UFrmL/czUAAAAAElFTkSuQmCC"), loadImage("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAIAAAACACAMAAAD04JH5AAAAclBMVEX///8AAAAMDAwUFBRtbW20tLTKysrr6+s0NDSMjIzo6OgrKytzc3Py8vJKSkolJSWSkpIODg5jY2OlpaW7u7ubm5urq6tEREQaGhr29vZRUVHg4ODDw8Pc3Nw/Pz9VVVWAgIDU1NQ3NzdcXFx6enovLy+wzkUiAAACjElEQVR4nO2a63qCMAxAlU5QEEEUuSMivP8rjptS2iid0qmfOT/XfM0BHGlLZjMEQRAE+ShMXQBTXn79aAgQ6tIE4u1cgG0sTWAvJrBHARRAART4bIFEvUm8Fsk/nxe3p1BH60RskJsIXX+FcnsKkp5GBAQv8nGyEQFHtoCDAiiAAihwP38gXyAAE592LbYhW6B0u1QJnX9nKS2y09d0mTIqv5n+R2KGH0pAW6IACqAACqDAewtYGwaPQGHEY+OsaQRK1WTJoLiMC1PLSQToyI4FULeVBR/3M4nA+nGBeztNFEABFEABcQFgM7WDBHZc2N2NnrgAcVYMWQ7F5Rkb54BF688CkkABFEABFEABWsDcvEBgUGRjm8YdAthtHTbItR2gQp7Z8kQh3mWw4if2ND5M83jPaVoZAIEN0CcCPEciTSCFBPg7IFEAeATAcas8gRz6DfDLJKLKEli+WqAEPgEDbzN5AgYgoPOnEfIELEiA/9YhT8CHBPgTIYkC0TsK+FzYdh8EgWma2lMNdsH5OqHihS1r6E1YhOGxxuurUn4+hhsvTcOxtgUItVg3OH3jRB4FLZrbDfbYwawb1IHy6T4gYPPTeJcL3/Obvv4fg7plV+yJBYDt+TsJHL9eIESBFwtAi/wvE+BXp1MJXFfjQF8fJXD2LYYSOEMcoB449JXCYl3f6JpD2EFqoaxHbLu51hRFU6tp/xIldEGtnho7HzmcFixU00WgsoP3692iKopVVczzZVka1S3xfVJQw8B6Ejj3fAbg49HI9hwFUAAFUOB/BYDDDeUwqUDBCwyO6dzSal7QPcY027oL6pJQk9fJjOEVdjVKjxqSJAE2f0+h13M2XCok3NiIIMgX8guBhVuAILp8rwAAAABJRU5ErkJggg=="),
];
}
function setup(){
createCanvas(600, 600);
imageMode(CENTER);
}
function draw(){
background(255);
// update + draw enemies
for(let i = 0; i < enemies.length; i++){
enemies[i].update();
}
text("click to spawn", 10, 15);
}
function mousePressed(){
// pick a random image
let randomImage = images[int(random(images.length))];
// make a new enemy
enemies.push(new Enemy(randomImage, mouseX, mouseY));
}
class Enemy {
constructor(skin, x, y) {
this.skin = skin;
this.position = createVector(x, y);
this.velocity = createVector(random(-1,1), random(-1,1));
}
update(){
// add velocity
this.position.add(this.velocity);
// check borders and flip direction (roughly)
if(this.position.x < 0 || this.position.x > width ||
this.position.y < 0 || this.position.y > height){
this.velocity.mult(-1);
}
// render
push();
blendMode(MULTIPLY);
image(this.skin, this.position.x, this.position.y);
pop();
}
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.3.0/p5.min.js"></script>
在上面的示例中,我使用 base64 编码的图像来避免 CORS 问题,但是您应该加载自定义图像。
这个想法是将图像全局存储,以便您可以在以后实例化新敌人时重新使用它们。 (如果你有几个图像,每个图像应该做单独的变量,否则数组会更容易管理)
Enemy
构造函数中的通知仅接收对先前加载图像的引用:
// when declared
constructor(skin, x, y) {
this.skin = skin;
// when intantiated
new Enemy(randomImage, mouseX, mouseY)
唯一剩下要做的就是根据需要渲染图像:
image(this.skin, this.position.x, this.position.y);
更新 根据评论和共享代码,每个敌人共享相同的 gif,以相同的速率同步更新相同的帧数。
一个选项是用同一图像的多个副本填充 images
数组,基本上每个敌人都重新加载一次 gif,尽管这看起来很浪费。
不幸的是 p5.Image.get()
returns 当前帧的快照并且没有用于克隆加载的 gif 的神奇功能,但是未记录的 gifProperties
包含手动执行此操作所需的数据:
let images;
let enemies = [];
function preload(){
// load images and store them in an array
images = [
loadImage("data:image/gif;base64,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"),
];
}
function setup(){
createCanvas(600, 600);
imageMode(CENTER);
}
function draw(){
background(255);
// update + draw enemies
for(let i = 0; i < enemies.length; i++){
enemies[i].update();
}
text("click to spawn", 10, 15);
}
function mousePressed(){
// pick a random image
let randomImage = images[int(random(images.length))];
// offset the start frame of each enemy by one
let startFrame = enemies.length % randomImage.numFrames();
// make a new enemy
enemies.push(new Enemy(cloneGif(randomImage,startFrame), mouseX, mouseY));
}
function cloneGif(gif, startFrame){
let gifClone = gif.get();
// access original gif properties
gp = gif.gifProperties;
// make a new object for the clone
gifClone.gifProperties = {
displayIndex: gp.displayIndex,
// we still point to the original array of frames
frames: gp.frames,
lastChangeTime: gp.lastChangeTime,
loopCount: gp.loopCount,
loopLimit: gp.loopLimit,
numFrames: gp.numFrames,
playing: gp.playing,
timeDisplayed: gp.timeDisplayed
};
// optional tweak the start frame
gifClone.setFrame(startFrame);
return gifClone;
}
class Enemy {
constructor(skin, x, y) {
this.skin = skin;
this.x = x;
this.y = y;
}
update(){
image(this.skin, this.x, this.y);
}
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.3.0/p5.min.js"></script>
cloneGif
生成一个新对象(这意味着新的起始帧等),但是它应该指向原始 gif 的 frames
(其中包含像素 / ImageData
)
现在每个敌人都从一个新的帧开始。
我注意到更改延迟似乎会影响所有敌人:一旦将延迟设置为 1,底层图像数据似乎就会以相同的速率复制。
如果您需要不同的延迟时间,您仍然可以访问相同的 gif 帧 ImageData
,而不是依赖 p5.Image
来控制 gif 播放(例如 setFrame()
/ delay()
) 你需要手动管理它并渲染到 p5 的 canvas:
let images;
let enemies = [];
function preload(){
// load images and store them in an array
images = [
loadImage("data:image/gif;base64,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"),
];
}
function setup(){
createCanvas(600, 600);
imageMode(CENTER);
}
function draw(){
background(255);
// update + draw enemies
for(let i = 0; i < enemies.length; i++){
enemies[i].update();
}
text("click to spawn", 10, 15);
}
function mousePressed(){
// pick a random image
let randomImage = images[int(random(images.length))];
// make a new enemy
let enemy = new Enemy({frames: randomImage.gifProperties.frames, gifWidth: randomImage.width, gifH: randomImage.height}, mouseX, mouseY);
// pick a random start frame
enemy.currentFrame = int(random(images.length));
// pick a random per gif frame delay (e.g. the larger the number the slower the gif will play)
enemy.frameDelay = int(random(40, 240));
enemies.push(enemy);
}
class Enemy {
constructor(gifData, x, y) {
this.frames = gifData.frames;
this.offX = -int(gifData.gifWidth * 0.5);
this.offY = -int(gifData.gifHeight * 0.5);
this.currentFrame = 0;
this.numFrames = this.frames.length;
this.frameDelay = 100;
this.lastFrameUpdate = millis();
this.x = x;
this.y = y;
}
update(){
let millisNow = millis();
// increment frame
if(millisNow - this.lastFrameUpdate >= this.frameDelay){
this.currentFrame = (this.currentFrame + 1) % this.numFrames;
this.lastFrameUpdate = millisNow;
}
// render directly to p5's canvas context
drawingContext.putImageData(this.frames[this.currentFrame].image, this.x + this.offX, this.y + this.offY);
}
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.3.0/p5.min.js"></script>
另请注意 p5.Image 在透明度方面做得很好(即使原始 gif 缺少它):如果走这条较低级别的路线,您需要手动解决这个问题。
我有一个 class(我们称它为“敌人”),我希望它们在离我足够近时攻击我(它会显示动画 gif,看起来像一口咬人)。
除了我能弄清楚的唯一方法是将 loadImage("attack.gif"), 放在 class 中,我已经完成了所有这些工作。这变得非常快,因为每次敌人出现时,它都必须重新加载该 gif。
我尝试在我的 class 中使用从 setup() 加载的 gif,但他们的所有攻击都是同步的。
还有其他方法吗?
您可以预加载 gif(或多个 gif)并将它们存储在一个数组中,稍后您可以在多个地方重新使用该数组(例如,在您的情况下是多个生成的敌人)。
这里有一个基本的例子来演示:
- 正在将图像加载到数组中
- 使用加载的图像在屏幕上(重新)生成对象
let images;
let enemies = [];
function preload(){
// load images and store them in an array
images = [
loadImage("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAIAAAACACAMAAAD04JH5AAAAPFBMVEX///8AAADf399fX19/f38/Pz+/v78fHx+fn58XFxeHh4cPDw+np6fX19fHx8evr6+3t7dvb2/39/dPT08jn1NmAAAA+UlEQVR4nO3ZSRKCMBBAUQnzPHj/u7qwOwtS0QhBwfpviaH9bDTG2w0AAAAAgNNLE2HC7zF6T0oAAQT8Q0CTC1M8Df61gywxek8TIcAq5anKXUsIIICACwaMmWhlept5OUvGCAH2o3iLqN8FBBBAwJcDJt3U1Dqsk+1O538/Z0mtU6aPAypnuu4JjfNKwJKKAAIIuEzAi+lxvPuFSQABBJwnoHY2JFu4U4ID7GYqzpYs+KiLAAIIOHPA7D+aWZsPCdh1Wk4AAQRcJ2CRQ5ai1yu9XrnnK/YfqWb9SuqfsoQ/xpoehSf+x1PHHtcTQAABPwsAAAAAAOAYD7+UFrmL/czUAAAAAElFTkSuQmCC"), loadImage("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAIAAAACACAMAAAD04JH5AAAAclBMVEX///8AAAAMDAwUFBRtbW20tLTKysrr6+s0NDSMjIzo6OgrKytzc3Py8vJKSkolJSWSkpIODg5jY2OlpaW7u7ubm5urq6tEREQaGhr29vZRUVHg4ODDw8Pc3Nw/Pz9VVVWAgIDU1NQ3NzdcXFx6enovLy+wzkUiAAACjElEQVR4nO2a63qCMAxAlU5QEEEUuSMivP8rjptS2iid0qmfOT/XfM0BHGlLZjMEQRAE+ShMXQBTXn79aAgQ6tIE4u1cgG0sTWAvJrBHARRAART4bIFEvUm8Fsk/nxe3p1BH60RskJsIXX+FcnsKkp5GBAQv8nGyEQFHtoCDAiiAAihwP38gXyAAE592LbYhW6B0u1QJnX9nKS2y09d0mTIqv5n+R2KGH0pAW6IACqAACqDAewtYGwaPQGHEY+OsaQRK1WTJoLiMC1PLSQToyI4FULeVBR/3M4nA+nGBeztNFEABFEABcQFgM7WDBHZc2N2NnrgAcVYMWQ7F5Rkb54BF688CkkABFEABFEABWsDcvEBgUGRjm8YdAthtHTbItR2gQp7Z8kQh3mWw4if2ND5M83jPaVoZAIEN0CcCPEciTSCFBPg7IFEAeATAcas8gRz6DfDLJKLKEli+WqAEPgEDbzN5AgYgoPOnEfIELEiA/9YhT8CHBPgTIYkC0TsK+FzYdh8EgWma2lMNdsH5OqHihS1r6E1YhOGxxuurUn4+hhsvTcOxtgUItVg3OH3jRB4FLZrbDfbYwawb1IHy6T4gYPPTeJcL3/Obvv4fg7plV+yJBYDt+TsJHL9eIESBFwtAi/wvE+BXp1MJXFfjQF8fJXD2LYYSOEMcoB449JXCYl3f6JpD2EFqoaxHbLu51hRFU6tp/xIldEGtnho7HzmcFixU00WgsoP3692iKopVVczzZVka1S3xfVJQw8B6Ejj3fAbg49HI9hwFUAAFUOB/BYDDDeUwqUDBCwyO6dzSal7QPcY027oL6pJQk9fJjOEVdjVKjxqSJAE2f0+h13M2XCok3NiIIMgX8guBhVuAILp8rwAAAABJRU5ErkJggg=="),
];
}
function setup(){
createCanvas(600, 600);
imageMode(CENTER);
}
function draw(){
background(255);
// update + draw enemies
for(let i = 0; i < enemies.length; i++){
enemies[i].update();
}
text("click to spawn", 10, 15);
}
function mousePressed(){
// pick a random image
let randomImage = images[int(random(images.length))];
// make a new enemy
enemies.push(new Enemy(randomImage, mouseX, mouseY));
}
class Enemy {
constructor(skin, x, y) {
this.skin = skin;
this.position = createVector(x, y);
this.velocity = createVector(random(-1,1), random(-1,1));
}
update(){
// add velocity
this.position.add(this.velocity);
// check borders and flip direction (roughly)
if(this.position.x < 0 || this.position.x > width ||
this.position.y < 0 || this.position.y > height){
this.velocity.mult(-1);
}
// render
push();
blendMode(MULTIPLY);
image(this.skin, this.position.x, this.position.y);
pop();
}
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.3.0/p5.min.js"></script>
在上面的示例中,我使用 base64 编码的图像来避免 CORS 问题,但是您应该加载自定义图像。 这个想法是将图像全局存储,以便您可以在以后实例化新敌人时重新使用它们。 (如果你有几个图像,每个图像应该做单独的变量,否则数组会更容易管理)
Enemy
构造函数中的通知仅接收对先前加载图像的引用:
// when declared
constructor(skin, x, y) {
this.skin = skin;
// when intantiated
new Enemy(randomImage, mouseX, mouseY)
唯一剩下要做的就是根据需要渲染图像:
image(this.skin, this.position.x, this.position.y);
更新 根据评论和共享代码,每个敌人共享相同的 gif,以相同的速率同步更新相同的帧数。
一个选项是用同一图像的多个副本填充 images
数组,基本上每个敌人都重新加载一次 gif,尽管这看起来很浪费。
不幸的是 p5.Image.get()
returns 当前帧的快照并且没有用于克隆加载的 gif 的神奇功能,但是未记录的 gifProperties
包含手动执行此操作所需的数据:
let images;
let enemies = [];
function preload(){
// load images and store them in an array
images = [
loadImage("data:image/gif;base64,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"),
];
}
function setup(){
createCanvas(600, 600);
imageMode(CENTER);
}
function draw(){
background(255);
// update + draw enemies
for(let i = 0; i < enemies.length; i++){
enemies[i].update();
}
text("click to spawn", 10, 15);
}
function mousePressed(){
// pick a random image
let randomImage = images[int(random(images.length))];
// offset the start frame of each enemy by one
let startFrame = enemies.length % randomImage.numFrames();
// make a new enemy
enemies.push(new Enemy(cloneGif(randomImage,startFrame), mouseX, mouseY));
}
function cloneGif(gif, startFrame){
let gifClone = gif.get();
// access original gif properties
gp = gif.gifProperties;
// make a new object for the clone
gifClone.gifProperties = {
displayIndex: gp.displayIndex,
// we still point to the original array of frames
frames: gp.frames,
lastChangeTime: gp.lastChangeTime,
loopCount: gp.loopCount,
loopLimit: gp.loopLimit,
numFrames: gp.numFrames,
playing: gp.playing,
timeDisplayed: gp.timeDisplayed
};
// optional tweak the start frame
gifClone.setFrame(startFrame);
return gifClone;
}
class Enemy {
constructor(skin, x, y) {
this.skin = skin;
this.x = x;
this.y = y;
}
update(){
image(this.skin, this.x, this.y);
}
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.3.0/p5.min.js"></script>
cloneGif
生成一个新对象(这意味着新的起始帧等),但是它应该指向原始 gif 的 frames
(其中包含像素 / ImageData
)
现在每个敌人都从一个新的帧开始。
我注意到更改延迟似乎会影响所有敌人:一旦将延迟设置为 1,底层图像数据似乎就会以相同的速率复制。
如果您需要不同的延迟时间,您仍然可以访问相同的 gif 帧 ImageData
,而不是依赖 p5.Image
来控制 gif 播放(例如 setFrame()
/ delay()
) 你需要手动管理它并渲染到 p5 的 canvas:
let images;
let enemies = [];
function preload(){
// load images and store them in an array
images = [
loadImage("data:image/gif;base64,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"),
];
}
function setup(){
createCanvas(600, 600);
imageMode(CENTER);
}
function draw(){
background(255);
// update + draw enemies
for(let i = 0; i < enemies.length; i++){
enemies[i].update();
}
text("click to spawn", 10, 15);
}
function mousePressed(){
// pick a random image
let randomImage = images[int(random(images.length))];
// make a new enemy
let enemy = new Enemy({frames: randomImage.gifProperties.frames, gifWidth: randomImage.width, gifH: randomImage.height}, mouseX, mouseY);
// pick a random start frame
enemy.currentFrame = int(random(images.length));
// pick a random per gif frame delay (e.g. the larger the number the slower the gif will play)
enemy.frameDelay = int(random(40, 240));
enemies.push(enemy);
}
class Enemy {
constructor(gifData, x, y) {
this.frames = gifData.frames;
this.offX = -int(gifData.gifWidth * 0.5);
this.offY = -int(gifData.gifHeight * 0.5);
this.currentFrame = 0;
this.numFrames = this.frames.length;
this.frameDelay = 100;
this.lastFrameUpdate = millis();
this.x = x;
this.y = y;
}
update(){
let millisNow = millis();
// increment frame
if(millisNow - this.lastFrameUpdate >= this.frameDelay){
this.currentFrame = (this.currentFrame + 1) % this.numFrames;
this.lastFrameUpdate = millisNow;
}
// render directly to p5's canvas context
drawingContext.putImageData(this.frames[this.currentFrame].image, this.x + this.offX, this.y + this.offY);
}
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.3.0/p5.min.js"></script>
另请注意 p5.Image 在透明度方面做得很好(即使原始 gif 缺少它):如果走这条较低级别的路线,您需要手动解决这个问题。