接下来时间真的很紧,也顾不上结构了,只能就目前想到的功能,先以直觉的方式编写了,如果讲不太清楚还多多包涵!昨天的一定要先看唷,今天是接着作下去的
玩家从进到网页开始,一直到游戏结束,总共会经历四个场景:
调整一下昨天设计的:「点击画面任一处,重新长出一颗新的树」取名为MakeTree函式,方便我们在离开读取画面後,可以移除这个功能(事件):
startScreen.addEventListener('click', MakeTree);
function MakeTree(){
treeGrowth.Restore();
myTree = new Tree(WIDTH/2, 0.8 * HEIGHT, HEIGHT/6, 90, maxTimes);
}
树的生长1-1和读取进度条1-2昨天都完成了,这边继续添加选单淡入1-3的功能,是在按下开始键後始选单1.5秒(90帧)内淡入,覆盖整个画面,初始设定:
let opacity = new Trail(0, 0, false);
按下Start按钮时,移除MakeTree函式,接着设定转场效果:
Start.addEventListener("click", function(event){
// 设定
event.stopPropagation();
// 执行
startScreen.removeEventListener('click', MakeTree);
Start.style.display = "none";
opacity.NewTarget(1, 0, 90);
});
由於昨天有设置整个画面的click作为让树重新长出来的互动,如果点了Start按钮也会触发则显得奇怪,因此在点击事件中做阻挡冒泡的设定,这样点击时,这样就不算点击到整个startScreen
然後在昨天设计的动画框架中,添加1-3选单淡入,设定header的opacity:
function LoadingScreen(){
try{
Resize("#game-box", canvas, context, '#000');
clear(context);
// 1. 让树长出来
treeGrowth.NextFrame(1, -1, 3);
myTree.Transform();
myTree.Draw();
// 2. 读取进度条
if(loading.timer > 0){
loading.NextFrame(1, 0, 2);
let percent = Math.floor(loading.pointX * 100);
Start.style.width = minWidth + percent + "px";
Start.textContent = percent + "%";
}
else if(Start.disabled == true){
Start.textContent = "Start";
Start.disabled = false;
}
// 3. 选单淡入
if(opacity.timer > 0){
opacity.NextFrame(1, 0, 2);
let header = document.getElementsByTagName("header")[0];
header.style.opacity = opacity.pointX;
}
}catch(e){
console.log(e);
return;
}
loadingAnime = requestAnimationFrame(LoadingScreen);
}
NextFrame内部会自行判断timer是否已归零(动画结束),这边的if判断式在於限制dom的操作在90次(帧数frames=90),为了应该会尝试把它整合到NextFrame的原型方法中,今天没这个时间啦~~
2-1是衔接1-3,让选单淡入後,等待玩家点击Play,就让选单淡出,此过程从场景1换到场景2,因此在这边:
Play.addEventListener("click", function(event){
// 让玩家发现,刚刚开场的那颗树,已经默默地成长为参天大树
myTree = new Tree(WIDTH/2, 0.8 * HEIGHT, HEIGHT/2, 90, maxTimes);
// 设定淡出和运镜
opacity.NewTarget(0, 0, 90);
camera.NewTarget(0.1, 0.3, 120); // 第一次运镜到左下角
// 切换场景 1>2
cancelAnimationFrame(loadingAnime);
openingAnime = requestAnimationFrame(OpeningScreen);
});
直接创建了一颗新的树,严谨一点的作法可以在使用原型方法Transform,并在其中修改树根长度的参数,这样可以保证是同一棵树,且形状相同,不过今天没时间去修改了><
运镜起始点:
let camera = new Trail(0, 0, false);
运镜这边用偷吃步的作法,把四个运镜接起来,逻辑就是每次timer归零,就会进入载入下一次的运镜,然後把帧数当作编号,依序用120、121、122、123表示四种运镜,因此当编号120的运镜结束(timer归零)时,就会设定新的路径编号为121,以此类推,当然这样写有点占空间,未来可以再多花时间去思考能怎麽样包成更简单的函式:
function OpeningScreen(){
try{
Resize("#game-box", canvas, context, undefined);
clear(context);
// 1. 选单淡出
if(opacity.timer > 0){
opacity.NextFrame(1, 0, 2);
let header = document.getElementsByTagName("header")[0];
header.style.opacity = opacity.pointX;
}
// 2. 运镜
let x = camera.pointX * WIDTH;
let y = camera.pointY * HEIGHT * 1;
context.translate(x, y);
myTree.Transform();
myTree.Draw(context);
context.translate(-x, -y);
if(camera.timer > 0){
if(camera.period == 120){
camera.NextFrame(1, 1.5, 0);
}
else if(camera.period == 121){
camera.NextFrame(1, 0, 1);
}
else if(camera.period == 122){
camera.NextFrame(1, 1.5, 0);
}
else if(camera.period == 123){
camera.NextFrame(1, 0, 1.5);
}
}
else if(camera.period == 120){
camera.NewTarget(0, 1, 121); // 第二次运镜到正上方
}
else if(camera.period == 121){
camera.NewTarget(-0.3, 0.2, 122); // 第三次运镜到右下角
}
else if(camera.period == 122){
camera.NewTarget(0, 0.6, 123); // 第四次运镜到中间
}
else{
// 运镜结束,直接进入第三个场景:游戏进行画面
GamingAnime = requestAnimationFrame(GamingScreen);
// 直接在此处中断即可结束该开场动画
return;
}
}catch(e){
console.log(e);
return;
}
openingAnime = requestAnimationFrame(OpeningScreen);
}
以camera的座标去换算,运镜分别设定四个点,让镜头顺时针绕一圈,呈螺旋状(底部>左下>正上方>右下>中间)
做到这边我发现一个致命问题,接下来的第三个场景需要一个静态画布+动态画布,而我原先设计的动画框架,没有考虑到使用两个以上的画布的情况,因此有点难以扩充,搞了一下子发现事情不太对劲,要重新编修整个框架,包含Resize动态调整宽高的方式,这部份明天继续!
https://jerry-the-potato.github.io/Chapter5/
运镜的部分稍微有点粗糙,主要是因为这四个运镜都是直线,如果之後有把第四章附录的贝兹曲线继续做完,就可以应用在这上面,便再也不用担心运镜看起来卡卡的罗!
<<: [第二十一只羊] 迷雾森林舞会XV 建立村庄 游戏角色设定
程序码导读 点开我们的main.c档案,可以看到里面密密麻麻的注解,第一次看到还真令人害怕,但其实他...
前言: 再来就是建立和训练模型 程序码: 方案一 model = tf.keras.Sequenti...
前言 已经能从「台湾证券交易所」抓资料、存入 DB,接下来要做自动化处理 说明 由於我电脑没有 24...
今天要来看的是JavaScript 错误 - throw、try 和 catch: 1.try 语句...
「干部不强,我身上尽是汗水味; 干部太强,我身旁满是血腥味。」 年轻时候 待过的公司,共有三个部门...