[开发者心得] “数据结构课作业笔记”#009-数组-循环队列

[复制链接]
616 |0
脑的研发记录 发表于 2023-11-23 22:31:17 | 显示全部楼层 |阅读模式
本帖最后由 脑的研发记录 于 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)




回复

使用道具 举报

热门版块
快速回复 返回顶部 返回列表