本帖最后由 脑的研发记录 于 2023-11-23 22:29 编辑
“数据结构课作业笔记”#009-数组-循环队列
所用编辑器版本0.27.0.0
目录总览:
零.意外收获之后
壹.第一个改装数组
贰.所需语法
叁.应用说明
肆.代码之后的马后炮
伍.相关参考链接 (返回主站
零.意外收获之后
在改bug替换函数过程中,顺藤摸瓜接触了控制物体物理模拟的代码,原地生成方块碰撞四散,由喷泉方块数目所限,以此产生了新的需求,在新的需求下引入了重复使用的想法。
具体说明新需求的背景:内存有限;就是产生一个方块需要一些内存,电脑内存不是无限的,所以产生的方块数目不是无限的,所以方块喷泉会枯竭。但是如果挤出的方块重新回到喷泉,就能看起来像无限喷涌一样。不能真正的无限——重复使用的起点——用有限来模拟不假思索地的无限需求。有限造无限,不停重复使用;场景里的方块循环使用,代码里的数组重复使用。
由此,我们有了以后帖子都会稳定的概念:重复使用。并在前面的代码中不自觉的敲代码中加强了这个概念:从一个方块,到一排方块,到地刺,到冲击波。量变到质变,需要满足人的需求才能被主动意识到,然后在新的为需求写代码过程中形成一个稳定的概念,然后用概念辅助代码开发。
参数
15:00-17:30文案上半段
21:00-22:28文案下半段
壹.第一个改装数组
“数据结构课作业笔记”#007-数组-从冲击层到按钮生成阶梯-排队队列 口袋方舟论坛|面向全年龄的UGC互动内容平台与交流社区 (ark.online)
代码更新,只是适配了函数,功能还是原来的功能,而且不用等点“预加载方块”按钮51次出bug,点11次就可以。
复制粘贴直接跑,按钮点击11次,观察和预期的不同。
class ooh {
n: Model;
id: string;
}
// class ooh {
// n: GameObject;
// id: string;
// }
@Component
export default class adds extends Script {
protected build: Array<ooh> = new Array(10);
// 100改小成50,控制距离
// 50改10,更快出bug
protected i: number = -1;
protected j: number = 0;
protected k: number = 0;
/** 当脚本被实例后,会在第一帧更新前调用此函数 */
protected onStart(): void {
AssetUtil.asyncDownloadAsset("197386");
for (var j = 0; j < this.build.length; j++) {
this.build[j] = new ooh();
}
// 创建一个UI对象
let ui = UserWidget.newObject();
// 创建一个画布组件
let rootCanvas = Canvas.newObject()
rootCanvas.size = new Vector2(1920, 1080);
rootCanvas.position = Vector2.zero
// 将Ui的根画布设置为rootCanvas
ui.rootContent = rootCanvas
// 创建一个UI按钮添加到画布上
let flybtn = StaleButton.newObject(rootCanvas)
flybtn.position = new Vector2(1900, 600)
flybtn.size = new Vector2(200, 100)
flybtn.text = "预加载方块"
flybtn.renderOpacity = 0.7
// 将UI桌面添加到屏幕上
ui.addToViewport(1)
flybtn.onClicked.add(() => {
flybtn.text = "this.i增加"
// let obj = GameObject.spawn("197386",{
// replicates: true,
// transform: new Transform(new Vector(0, this.k * 100, this.k * 25),
// new Rotation(0, 0, 0),
// new Vector(1, 1, 1))
// //写需要设置的Transform
// });
// this.in(obj);
let box = GameObject.spawn("197386", {
transform: new Transform(new Vector(500, 0+this.k*100, this.k*100), new Rotation(0, 0, 0), new Vector(1, 1, 1)),
replicates: true
}) as Model;
this.in(box);
this.k++;
flybtn.text = "this.i增加完毕"
setTimeout(() => {
flybtn.text = "预加载方块"
}, 500);
})
// 又创建一个UI按钮添加到画布上
let outbtn = StaleButton.newObject(rootCanvas)
// 按钮位置
outbtn.position = new Vector2(1900, 100)
// 按钮大小
outbtn.size = new Vector2(200, 100)
// 按钮名字
outbtn.text = "出院"
outbtn.onClicked.add(() => {
outbtn.text = "鸡汤来喽!"
var have: ooh = this.out();
have.n.setCollision(PropertyStatus.Off, true);
have.n.setVisibility(PropertyStatus.Off, true);
setTimeout(() => {
outbtn.text = "出院"
}, 500);
})
}
// public in(obj: GameObject) {
// this.i++;
// this.build[this.i].n = obj;
// }
public in(obj: Model) {
this.i++;
this.build[this.i].n = obj;
}
// public show(): ooh {
// this.i++;
// var have = this.build[this.i];
// return have;
// }
// public out(): ooh {
// var have = this.build[this.i];
// this.i--;
// return have;
// }
// public out(): ooh {
// var have = this.build[this.i];
// this.i++;
// return have;
// }
public out(): ooh {
var have = this.build[this.j];
this.j++;
return have;
}
}
BUG说明:具体来说就是数组不够用了,原来的数组长度50,能存50个方块,但是现在是长度是10,只能存10个方块。第十一个方块生成了,但是数组里没有第11位这个概念,所以出现意料之外的情况——方块不能继续产生。
当然,如果没有对接需求,当然说不上是bug,但是确实是与预期相悖。
目前这里希望方块能够不断生长,生成的方块也能够消失。
如果说除了上面的需求还有代码能跑,其他的问题不考虑的话。令人震惊的操作是把数组长度从10改成10000000000,如果代码不报错,确实真的行。
当然再增加新的需求,内存有限,数组长度最大只能是100。(数组缩水原因包括但不限于究竟是上司出难题导致,还是内存影响游戏安装包大小,还是穿越到旧时代做一个游戏程序员要面对内存不足的时代难题)
难绷,意识不到重复使用100数组,这一次帖子可能就被炒鱿鱼了,但是翻看已有的帖子,“数据结构课作业笔记”#008-数组-方块喷泉-循环队列预兆 口袋方舟论坛|面向全年龄的UGC互动内容平台与交流社区 (ark.online),已经有了一个有限模拟无限的概念。如果不是上司刁难,好像改改旧代码确实满足需求。
想起来数学书上的“注意到”,如今咱也不得不用这个词语了
注意到008帖子的代码及其上下紧接着的解释文案。代码如下
this.j++;
if (this.j > 10) {
// this.build[this.k].n.physicsEnabled = false;
// this.build[this.k].n.setCollision(PropertyStatus.Off, true);
// this.build[this.k].n.setVisibility(PropertyStatus.Off, true);
// this.build[this.k].n.localTransform.position.x = 500;
// this.build[this.k].n.localTransform.position.y = 0;
// this.build[this.k].n.localTransform.position.z = 100;
this.build[this.k].n.localTransform=new Transform(new Vector(500, 0, 100), new Rotation(0, 0, 0), new Vector(1, 1, 1));
this.k++;
this.k=this.k%10;
}
注意到实现回到数组开头的一行功能的代码,也是实现重复调用方块数据的代码:
this.k=this.k%10;
“%来了。”
“就是this.k换成this.i,this.j是吧?”
只增加两行代码。极为简单的找不同。
// public in(obj: GameObject) {
// this.i++;
// this.build[this.i].n = obj;
// }
public in(obj: Model) {
this.i++;
this.i=this.i%10;
this.build[this.i].n = obj;
}
// public show(): ooh {
// this.i++;
// var have = this.build[this.i];
// return have;
// }
// public out(): ooh {
// var have = this.build[this.i];
// this.i--;
// return have;
// }
// public out(): ooh {
// var have = this.build[this.i];
// this.i++;
// return have;
// }
public out(): ooh {
var have = this.build[this.j];
this.j++;
this.j=this.j%10;
return have;
}
这个时候跑代码,点五次生成按钮,点四次删除按钮,可以发现方块延伸的高度增加很多。
一步步来说,就是this.j生成0,1,2,3,4,5,6,7,8,9,对应数组的10个项都暂存了方块数据。当j成为第10个的时候,this.j就回到数组开头的位置,而只要this.j回到开头之前,点击过一次删除按钮,那么数组的开头项就不能再被删除按钮控制,然后this.j又指定了这数组开头,于是第十一次点击生成的时候,数组最开头的那一项就被覆盖了。这个时候旧方块不仅在游戏里消失了,旧方块的数据也在代码里找不到了。
这就是循环队列——this.i删除,就是队头;this.j,加入数据,就是队尾。this.i,this.j永远不超过数组的长度,被%10变成小于10的数字,底层原理是整除除不尽,剩下的余数重新赋值覆盖了this.i,this.j。
不过,按钮点的多了,点了11次增加按钮,然后在点击1次删除按钮,就不会像一开始的先增加的先删除。这是因为队头队尾相撞了,队尾已经超过了对头一圈又回来了,正好对头队尾都指定在数组开头第一个数据。还没删除旧方块,就自动覆盖了。
新需求诞生了,改bug,只要控制队尾不会“相撞”队头,控制this.i==this.j不发生就行。
public out(): ooh {
var have = this.build[this.j];
this.j++;
this.j=this.j%10;
if(this.j==this.i){
this.j--;
}
return have;
}
够简单,但是不够靠谱
// public out(): ooh {
// var have = this.build[this.j];
// this.j++;
// this.j=this.j%10;
// return have;
// }
public out(): ooh {
var have = this.build[this.j];
this.j++;
if(this.j==this.i){
this.j--;
}
this.j=this.j%10;
return have;
}
这样,加了一个复位,如果队尾赶上队头,队尾就倒退,其他操作不干。
同样,加了一个复位,如果队头赶上队尾,队头就倒退,其他操作不干。
// public in(obj: Model) {
// this.i++;
// this.i=this.i%10;
// this.build[this.i].n = obj;
// }
public in(obj: Model) {
this.i++;
if (this.i == this.j) {
this.i--;
}
else {
this.build[this.i].n = obj;
this.i = this.i % 10;
}
}
但是代码跑起来保存,只生成不删除,直觉告诉我改了哪里,哪里出bug。重新穷举读代码,发现一开始this.i=-1,第一个数据加入之后,this.i=0,this.j=0,直接触发if语句导致this.i--倒退。
于是再修改,合并this.i++和this.j--无效作用项目,把this.i++的其他影响单独摘出来放进到else里面。
public in(obj: Model) {
if (this.i == this.j) {
}
else {
this.i++;
this.build[this.i].n = obj;
this.i = this.i % 10;
}
}
又出bug了,原来是this.i的数字大小要先+1,演算看看有没有撞车到队尾,之前压根没注意this.i++的影响范围不止一个else
public in(obj: Model) {
if (this.i+1 == this.j) {
}
else {
this.i++;
this.build[this.i].n = obj;
this.i = this.i % 10;
}
}
跑代码,发现每10次生成会有一个方块不能消失,均匀间隔,和10有关,就看%10,穷举读数,发现数组里没有包含下标为10的数据项,也就是到this.i=9时,已经完成了10次,第十次数据生成之后就要从9回到0,而9%10=9,然后this.i++然后再变成10,数据存进一个数组不包含的位置里导致数据丢失。于是只能生成,但是不能删除,
再修改,%10变成%9
// public in(obj: Model) {
// this.i++;
// this.i=this.i%10;
// this.build[this.i].n = obj;
// }
public in(obj: Model) {
if (this.i + 1 == this.j) {
}
else {
this.i++;
this.build[this.i].n = obj;
this.i = this.i % 9;
}
}
// public show(): ooh {
// this.i++;
// var have = this.build[this.i];
// return have;
// }
// public out(): ooh {
// var have = this.build[this.i];
// this.i--;
// return have;
// }
// public out(): ooh {
// var have = this.build[this.i];
// this.i++;
// return have;
// }
// public out(): ooh {
// var have = this.build[this.j];
// this.j++;
// this.j=this.j%10;
// return have;
// }
public out(): ooh {
if (this.j == this.i) {
} else {
var have:ooh = this.build[this.j];
this.j++;
this.j = this.j % 9;
}
return have;
}
但是跑代码又出现每10个就不能删除一个方块,感觉可能是this.i的覆盖数值比this.j大,把i++和下一行代码调换顺序,成了
public in(obj: Model) {
if (this.i + 1 == this.j) {
}
else {
// this.i++;
this.build[this.i].n = obj;
this.i++;
this.i = this.i % 9;
}
}
重新跑,发现成功了,人更加奇怪了,真的是this.i数值的问题。于是从1数到10,验证数据,数到9的时候,this.i是先存再加1,于是8+1=9,build[this.i]永远到不了builtd[9],this.i一变成9就立马恢复成0了。这样代码和out函数对称,原来是out函数少只能删除0-8的数据,于是就重新改写,数组0-9都能删除
public out(): ooh {
if (this.j == this.i) {
} else {
this.j++;
var have:ooh = this.build[this.j];
// this.j++;
this.j = this.j % 10;
}
return have;
}
跑代码但是出现之前的方块删除顺序出错,先出来的却没有先删除。 还是第9次,顺序改成先加再存,this.j是9的时候,需要判断下一位是不是this.i的指定的位置,避免撞车。
最后修改this.j+1==this.i
public out(): ooh {
if (this.j+1 == this.i) {
} else {
this.j++;
var have:ooh = this.build[this.j];
// this.j++;
this.j = this.j % 9;
}
return have;
}
至此,代码完整,容量是10的队列,最多能存9个数据,其中一个因为this.i+1==this.j而不能使用。
最终成果
class ooh {
n: Model;
id: string;
}
// class ooh {
// n: GameObject;
// id: string;
// }
@Component
export default class adds extends Script {
protected build: Array<ooh> = new Array(10);
// 100改小成50,控制距离
// 50改10,更快出bug
protected i: number = 0;
protected j: number = 0;
protected k: number = 0;
/** 当脚本被实例后,会在第一帧更新前调用此函数 */
protected onStart(): void {
AssetUtil.asyncDownloadAsset("197386");
for (var j = 0; j < this.build.length; j++) {
this.build[j] = new ooh();
}
// 创建一个UI对象
let ui = UserWidget.newObject();
// 创建一个画布组件
let rootCanvas = Canvas.newObject()
rootCanvas.size = new Vector2(1920, 1080);
rootCanvas.position = Vector2.zero
// 将Ui的根画布设置为rootCanvas
ui.rootContent = rootCanvas
// 创建一个UI按钮添加到画布上
let flybtn = StaleButton.newObject(rootCanvas)
flybtn.position = new Vector2(1900, 600)
flybtn.size = new Vector2(200, 100)
flybtn.text = "预加载方块"
flybtn.renderOpacity = 0.7
// 将UI桌面添加到屏幕上
ui.addToViewport(1)
flybtn.onClicked.add(() => {
flybtn.text = "this.i增加"
// let obj = GameObject.spawn("197386",{
// replicates: true,
// transform: new Transform(new Vector(0, this.k * 100, this.k * 25),
// new Rotation(0, 0, 0),
// new Vector(1, 1, 1))
// //写需要设置的Transform
// });
// this.in(obj);
let box = GameObject.spawn("197386", {
transform: new Transform(new Vector(500, 0 + this.k * 100, this.k * 100), new Rotation(0, 0, 0), new Vector(1, 1, 1)),
replicates: true
}) as Model;
this.in(box);
this.k++;
flybtn.text = "this.i增加完毕"
setTimeout(() => {
flybtn.text = "预加载方块"
}, 500);
})
// 又创建一个UI按钮添加到画布上
let outbtn = StaleButton.newObject(rootCanvas)
// 按钮位置
outbtn.position = new Vector2(1900, 100)
// 按钮大小
outbtn.size = new Vector2(200, 100)
// 按钮名字
outbtn.text = "出院"
outbtn.onClicked.add(() => {
outbtn.text = "鸡汤来喽!"
var have: ooh = this.out();
have.n.setCollision(PropertyStatus.Off, true);
have.n.setVisibility(PropertyStatus.Off, true);
setTimeout(() => {
outbtn.text = "出院"
}, 500);
})
}
// public in(obj: GameObject) {
// this.i++;
// this.build[this.i].n = obj;
// }
// public in(obj: Model) {
// this.i++;
// this.i=this.i%10;
// this.build[this.i].n = obj;
// }
public in(obj: Model) {
if (this.i + 1 == this.j) {
}
else {
this.i++;
this.build[this.i].n = obj;
// this.i++;
this.i = this.i % 9;
}
}
// public show(): ooh {
// this.i++;
// var have = this.build[this.i];
// return have;
// }
// public out(): ooh {
// var have = this.build[this.i];
// this.i--;
// return have;
// }
// public out(): ooh {
// var have = this.build[this.i];
// this.i++;
// return have;
// }
// public out(): ooh {
// var have = this.build[this.j];
// this.j++;
// this.j=this.j%10;
// return have;
// }
public out(): ooh {
if (this.j+1 == this.i) {
} else {
this.j++;
var have:ooh = this.build[this.j];
// this.j++;
this.j = this.j % 9;
}
return have;
}
}
注意到代码对称
public in(obj: Model) {
if (this.i + 1 == this.j) {
}
else {
this.i++;
this.build[this.i].n = obj;
// this.i++;
this.i = this.i % 9;
}
}
public out(): ooh {
if (this.j+1 == this.i) {
} else {
this.j++;
var have:ooh = this.build[this.j];
// this.j++;
this.j = this.j % 9;
}
return have;
}
像是同一个代码复制粘贴
这是以前的非循环的队列的代码,像是倒影。
// public in(obj: GameObject) {
// this.i++;
// this.build[this.i].n = obj;
// }
// public out(): ooh {
// var have = this.build[this.j];
// this.j++;
// return have;
// }
这是由于两段代码的起始点不同,第一段代码起始this.i,this.j都是0,而下面的代码是this.i=-1,this.j=0,this.i落后this.j
而原始代码的队头尾都从0开始,数据加入的顺序,同样是数据删除的顺序,就像是“加入一个删除标记”。这就是代码看上去重复的简短解释。
也解答了一开始写数据的问题,this.i,this.j的起点对其他部分代码的影响究竟是怎样的,如何确定共同的起点是比较靠谱的写法,从混乱穷举逐渐到有序,可以理解无中生有的过程。但是从有序去面对混乱,就难以相信无中生有的过程。
贰.所需语法
无
叁.应用说明
用队列暂存数据,记录数据的顺序。
如击落广播。
肆.代码之后的马后炮
实际上之前的数组都是按照一次性使用的默认想法,相当于是这样的“一次性使用”重复好几下,正如一个火箭发射五次和五火箭轮流发射一次,没有相同的概念,就只有不断的复刻:发射一次火箭造一次,用一个方块就生成一个。没有需求促使提出循环概念,也没有需求促使“循环”的概念和“重复使用”的概念分离,更好的指导代码开发。
也就是说,在之前的代码中,提不提循环的概念对实现的代码没有影响,只是“重复使用”就足以理解过去的数组帖子。因为不会出现无法理解的BUG和难绷的代码。重复使用已经理解成只是不断的复制代码就能实行,纯纯的增加数字数目,增加动态生成的方块数量,其他等着看效果就行。但是在之后的开发中,重复是结果,一步步改bug过程中形成这个结果。
循环的概念是两个不同功能,最后实现的是同一个顺序:队头尾都从0开始,数据加入的顺序,同样也是数据删除的顺序,就像是“加入一个删除标记”。
代码开发,基本探索方法是“重复使用”,
重复使用的结果会有新的效果,是循环。
不敲代码猜到是循环,写代码的时候一边重复使用数据,一边把复制粘贴的代码改写。
想看重复使用的效果,就敲代码复现,没有预料,就是重复使用。
伍.相关参考链接 (返回主站
普通大学生“数据结构”课作业笔记#000-目录-逻辑结构与存储结构 口袋方舟论坛|面向全年龄的UGC互动内容平台与交流社区 (ark.online)
|