如何在PApplet中正确调用带for循环的draw方法完成绘图
问题根因
代码存在4个核心问题,导致draw()运行时只显示背景:
- 静态成员
processing从未赋值:setup()方法的入参名和类的静态成员processing重名,方法内操作的是局部入参,没有把图形窗口的引用赋值给类的静态变量,draw()调用时拿到的processing是空值,直接触发空指针,后续渲染逻辑根本跑不起来 - 背景图只在setup()绘制了一次:draw()是无限循环执行的帧回调,每轮执行前都会清空画布,你没在draw()开头重绘背景,就算鱼能画出来也会被清空
- 鱼类对象重复创建、随机数生成器重复初始化:你把Fish对象初始化、Random实例创建的逻辑全放在了draw()的循环里,等于每帧都要新建8个Fish对象、8个Random实例,不仅平白浪费性能,前一个new出来的居中Fish对象直接被后一个随机位置的对象覆盖,完全是无效操作,高频创建对象还可能导致渲染阻塞
- 缺少空值判断:fishes数组初始化后所有元素默认是null,你没做非空判断就直接调用
fishes[i].draw(),很容易触发空指针异常中断程序。另外main方法里打印fishes[0]得到null是正常现象——Utility.startApplication()是异步启动程序,main线程不会等setup执行完就往下跑,这行代码没有实际意义可以直接删掉。
setup()与draw()的正确执行逻辑
Processing/PApplet的生命周期是固定的,不要乱改执行顺序:
- 程序启动后仅执行一次setup():只做一次性初始化操作——给全局变量赋值、加载图片/字体等资源、初始化数组、生成初始的业务对象(比如这里的8条鱼)
- setup()执行完成后无限循环执行draw():每帧执行时先重绘背景清空上一帧内容,再遍历所有对象做状态更新、渲染,所有和绘制相关的操作都要放在draw()里。
修正后的完整代码
import java.util.Random; // 记得导入你项目中PApplet、PImage、Utility、Fish对应的包路径 public class FishPAppletTest { private static PApplet processing; private static PImage backgroundImage; private static Fish[] fishes; private static Random randGen; public static void main(String[] args) { Utility.startApplication(); } /** * 程序初始化回调,仅启动时执行一次 * @param processing 图形渲染窗口实例 */ public static void setup(PApplet processing) { // 给全局静态processing赋值,解决空指针问题 FishPAppletTest.processing = processing; // 加载背景资源 backgroundImage = processing.loadImage("images/background.png"); // 初始化鱼数组、随机数生成器,随机数生成器全局只需要一个实例 fishes = new Fish[8]; randGen = new Random(); // 一次性初始化所有鱼对象,不要放到draw里每帧新建 for (int i = 0; i < fishes.length; i++) { int randomX = randGen.nextInt(processing.width); int randomY = randGen.nextInt(processing.height); fishes[i] = new Fish(processing, randomX, randomY); } } /** * 帧渲染回调,启动后无限循环执行 */ public static void draw() { // 每帧开头先画背景,清空上一帧的残留内容 processing.image(backgroundImage, processing.width / 2f, processing.height / 2f); // 遍历鱼数组,非空才执行渲染,避免空指针 for (Fish fish : fishes) { if (fish != null) { fish.draw(); } } } }
后续开发注意点
- 不要在draw()里做资源加载、对象初始化这类一次性操作,否则每帧重复创建对象会导致内存占用飙升、程序卡顿
- 如果后续要做鱼游动的动画,只需要在draw()遍历鱼的逻辑里更新每个鱼的坐标属性即可,不需要新建Fish对象
- 静态成员变量不要和方法入参重名,容易出现赋值给局部变量、全局变量没初始化的问题。
内容的提问来源于stack exchange,提问作者user19394712
相关产品推荐
相关产品推荐

