// PstarEngine 0.21.5 Java version // 1. Functions // 1.1. BugMessage // 2. System // 2.1. EventType // 2.2. PEvent // 2.3. ActionEvents // 2.4. SystemEvents // 3. Base Nodes // 3.1. Observable // 3.2. Observer // 3.3. NodeType // 3.4. Node // 3.5. Node2D // 3.6. PEngine // 3.7. Responsive // 3.8. RefSize // 3.9. Components // 3.9.1. ComponentType // 3.9.2. Component // 3.9.3. Component2D // 3.9.4. Sprite2D // 3.9.5. Rotation // 3.9.6. Timer // 3.9.7. Background (TODO cf JS) // 4. Physic Engine // 5. GUI // 5.1. Control // 5.2. GUIcomponent // 5.3. Themes // 5.3.1. ThemeType // 5.3.2. Theme // 5.4. Focus // 5.5. Languages // 5.W. Widgets // 5.W.x. a widget // 5.W.x.1. the widget (extends Control) // 5.W.x.2. GUIScript for FLAT // 5.W.x.y. GUIScript for ... // 5.W.1. Slider // 5.W.1.1. Slider class // 5.W.1.2. Slider GUIscript for FLAT // 6. special // 6.1. Preload // 1. Functions // 1.1. BugMessage void bugMessage(String message) { // TODO ; popWindow with application freezed with Palert(String message) println(message); } // 2. System // 2.1. EventType // cf TAB Application // 2.2. PEvent class PEvent { EventType eventType; Object source; PEvent(EventType eventType, Object source) { this.eventType=eventType; this.source=source; } } // 2.4. SystemEvents // singleton pengine.systemEvents class SystemEvents implements Observable { ArrayList listeners = new ArrayList<>(); // keyboard // dictionary : String key -> ActionType (enum)) HashMap Inputs = new HashMap<>(); // Implements Observable void listen(Node node) { listeners.add(node); } //void unListen(Node node); void notify(PEvent event) { for (Node node : listeners) { node.signal(event); } } // end Implements Observable // mouse void clicEvent() { notify(new PEvent(EventType.CLIC, this)); } // keyboard SystemEvents addInput(String s, EventType e) { Inputs.put(s, e); return this; } void run() { if (keyPressed) { //println("["+key+"] <"+keyCode+">"); switch(keyCode) { case 37 : keyboard("LEFT"); break; case 38 : keyboard("UP"); break; case 39 : keyboard("RIGHT"); break; case 40 : keyboard("DOWN"); break; case 155 : keyboard("INSERT"); break; case 36 : keyboard("HOME"); break; case 35 : keyboard("END"); break; case 17 : keyboard("CTRL"); break; case 16 : keyboard("SHIFT"); break; case 20 : keyboard("MAJ"); break; case 18 : keyboard("ALT"); break; case 27 : keyboard("ESC"); break; case 65406 : keyboard("ALTGR"); break; case 33 : keyboard("PAGEUP"); break; case 34 : keyboard("PAGEDOWN"); break; // problem with this key !!! do not use it !!! case 127 : keyboard("DEL"); break; default : switch(str(key)) { case "\n": keyboard("ENTER"); break; case "\b": keyboard("BACKSPACE"); break; case "\t": keyboard("TAB"); break; default : keyboard(str(key).toUpperCase()); } } } } //end Run() void keyboard(String k) { if (Inputs.containsKey(k)) { notify(new PEvent(Inputs.get(k), this)); } } } // End SystemEvents void mouseReleased() { pengine.systemEvents.clicEvent(); } // 3. Base Nodes // 3.1. Observable interface Observable { void listen(Node node); //void unListen(Node node); void notify(PEvent event); } // 3.2. Observer interface Observer { void signal(PEvent event); } // 3.3. NodeType enum NodeType { NULL, NODE, NODE2D, PRELOAD, TEST, REFSIZE, CONTROL, // Widgets type : WIDGETTEST, SLIDER; } // 3.4. Node class Node implements Observable, Observer { NodeType nodeType; Boolean isRoot = false; // Only PEngine class marks it as true String name; Boolean isDisabled, isFreezed, isToRemove; Boolean isReady=false; ArrayList children = new ArrayList<>(); Node parent; // nothing for root Node // Components ArrayList components = new ArrayList<>(); // implements Observable ArrayList listeners = new ArrayList<>(); Node(String name) { nodeType = NodeType.NODE; this.name=name; isDisabled = isFreezed = isToRemove = false; } String toString() { String toReturn = name+"("+nodeType+")"; for (Component component : components) { toReturn+="\n COMPONENT : "+component.name+"("+component.componentType+")\n"; } return toReturn; } void _printTreeHelp(int decal, String toPrint) { if (decal!=0) { for (int i=0; i"); } } print(toPrint); println(""); } void printTree() { _printTreeHelp(pengine.decalTree, toString()); pengine.decalTree+=1; for (Node child : children) { child.printTree(); } pengine.decalTree-=1; } // implements Observable void listen(Node node) { listeners.add(node); } // TODO in JS ! void unListen(Node node) { listeners.remove(node); } void notify(PEvent pEvent) { for (Node node : listeners) { node.signal(pEvent); } } // implements Observer void signal(PEvent pEvent) { for (Component component : components) { component.signal(this, pEvent); } } // end implementations void addChild(Node child) { children.add(child); child.parent=this; } Node addComponent(Component component) { // D.P. Decorator components.add(component); return this; } void _runReady() { ready(); for (Node child : children) { child._runReady(); } } void _runInMatrix() { for (Node child : children) { if (!child.isDisabled && !child.isToRemove) { child._runInMatrix(); } } } void _runOutMatrix(float delta) { process(delta); for (Node child : children) { if (!child.isDisabled && !child.isToRemove && !isFreezed) { child._runOutMatrix(delta); } } } void ready() { if (!isReady) { for (Component component : components) { component.ready(this); } isReady=true; } } void process(float delta) { for (Component component : components) { component.process(this, delta); } } void toRemove() { if (!isRoot) { isToRemove=true; } } void _remove() { if (!isRoot) { if (isToRemove) { parent.children.remove(this); } } } void setDisable(Boolean b) { if (!isRoot) { isDisabled = b; } } void setFreezed(Boolean b) { if (!isRoot) { isFreezed=b; for (Node child : children) { child.setFreezed(b); } } } } // 3.5. Node2D class Node2D extends Node { PVector position, globalPosition, size, globalSize; float rotation, globalRotation, scale, globalScale; // Components2D ArrayList components = new ArrayList(); Node2D(String name) { super(name); nodeType = NodeType.NODE2D; position=new PVector(0, 0); rotation=0; globalRotation=0; scale=1; globalScale=1; globalPosition = position.copy(); size=new PVector(10, 10); } Node2D setPosition(float x, float y) { position.set(x, y); globalPosition = position.copy(); return this; } Node2D setSize(float w, float h) { size.set(w, h); return this; } Node2D setScale(float s) { scale=s; return this; } PVector topLeft() { return new PVector(-size.x/2, -size.y/2); } PVector globalTopLeft() { return globalPosition.copy().add(topLeft()); } Node2D addComponent(Component2D component) { // D.P. Decorator components.add(component); return this; } void signal(PEvent pEvent) { for (Component2D component : components) { component.signal(this, pEvent); } } String toString() { String toReturn = name+"("+nodeType+"), position = ("+this.position.x+","+this.position.y+"), rotation = "+this.rotation+", scale = "+this.scale; for (Component2D component : components) { toReturn+="\n COMPONENT : "+component.name+"("+component.componentType+")\n"; } return toReturn; } void addChild(Node child) { if (child.nodeType==NodeType.NODE) { bugMessage("Node2D.addChild("+child.name+") : children can't be Node type !"); } else { super.addChild(child); } } void _runInMatrix() { push(); translate(this.position.x, this.position.y); rotate(this.rotation); scale(this.scale); this.display(); super._runInMatrix(); pop(); } void ready() { if (!isReady) { for (Component2D component : components) { component.ready(this); } isReady=true; } } void process(float delta) { for (Component2D component : components) { component.process(this, delta); } } void display() { if (pengine.debug) { push(); fill(255, 0, 0); stroke(255, 0, 0); strokeWeight(1); circle(0, 0, 10); textFont(font); textSize(20); strokeWeight(1); text(this.name, 10, 10); pop(); } for (Component2D component : components) { component.display(this); } } void globalComputations() { this.globalPosition = this.position.copy(); this.globalRotation = this.rotation; this.globalScale = this.scale; if (!this.isRoot) { if (this.parent.nodeType!=NodeType.NODE) { Node2D parentBis = (Node2D) parent; globalRotation += parentBis.globalRotation; globalScale *= parentBis.globalScale; globalPosition = parentBis.globalPosition.copy().add(position.copy().rotate(parentBis.globalRotation).mult(parentBis.globalScale)); } } globalSize = size.copy().mult(globalScale); } void _runOutMatrix(float delta) { globalComputations(); super._runOutMatrix(delta); } PVector _parentGlobalPosition() { if (this.parent.nodeType==NodeType.NODE) { return new PVector(0, 0); } else { Node2D parentBis = (Node2D) parent; return parentBis.globalPosition; } } // DON'T TRY TO USE D.P. DECORATOR HERE !!! void setGlobalPosition(float globalX, float globalY) { if (this.isRoot) { this.position.set(globalX, globalY); } else { PVector globalPosition=new PVector(globalX, globalY); position.set(globalPosition.sub(_parentGlobalPosition())); } } void setGlobalPositionX(float globalX) { this.setGlobalPosition(globalX, this.globalPosition.y); } void setGlobalPositionY(float globalY) { this.setGlobalPosition(this.globalPosition.x, globalY); } } // 3.6. PEngine class PEngine { Node root; Node sceneParent; Boolean isPreloadReady; float delta; float pMillis=0; // only for Java version ! color backgroundColor=color(0, 0, 0); // Preload Preload preload=new Preload(); Node2D preloadPreviousScene=null; // global variables Boolean hasRefSize=false; RefSize refSize; String userLang; int decalTree=0; Boolean debug=false; Theme theme; SystemEvents systemEvents = new SystemEvents(); // End global variables PEngine () { root = new Node("root"); root.isRoot=true; sceneParent=root; isPreloadReady = false; userLang="??"; // must be ask to the user as the JVM is Processing JVM most of the time ! theme = new Theme(); theme.addGUIcomponent(new SliderBASIC("SliderBASIC")); } PEngine setBackground(color bg) { backgroundColor=bg; return this; } PEngine setRefSize(color bgColor) { if (frameCount==0) { // setRefSize() called in setup() hasRefSize=true; refSize = new RefSize(bgColor); sceneParent = refSize; root.addChild(sceneParent); } else { bugMessage("pengine.setRefSize() must not be called in draw() !"); exit(); } return this; } void setRefSizeColor(color bgColor) { if (hasRefSize) { RefSize rs = (RefSize) sceneParent; rs.bgColor=bgColor; } else { bugMessage("PEngine.setRefSize() cannot change RefSize color : no RefSize !"); exit(); } } void run() { // / defaults imageMode(CENTER); textAlign(CENTER); // delta computation delta=(millis()-pMillis)/1000; pMillis=millis(); // Reading keyboard systemEvents.run(); if (root==null) { bugMessage("PEngine : root doesn't exist !"); exit(); } else { if (!isPreloadReady) { preloadPreviousScene=getScene(); setScene(preload); root._runReady(); isPreloadReady=true; } background(backgroundColor); root._runOutMatrix(delta); root._runInMatrix(); root._remove(); } } void printTree() { println(""); root.printTree(); println(""); decalTree=0; } void setScene(Node2D node2d) { sceneParent.children=new ArrayList<>(); sceneParent.addChild(node2d); if (frameCount!=0) { // setScene() not called in setup() node2d._runReady(); } } Node2D getScene() { if (sceneParent.children.size()!=0) { return (Node2D) sceneParent.children.get(0); } else { return null; } } } // 3.7. Responsive // only for JS ! // use // surface.setResizable(true); in setup() // windowRatio(,); in draw() //for Java // 3.8. RefSize // singleton pengine.refSize may be null if not used class RefSize extends Node2D { color bgColor; RefSize(color bgColor) { super("RefSize"); nodeType=NodeType.REFSIZE; this.bgColor=bgColor; setPosition(width/2, height/2); setSize(width, height); } void display() { push(); rectMode(CENTER); noStroke(); fill(this.bgColor); rect(0, 0, size.x, size.y); pop(); } } // 3.9. Components // 3.9.1. ComponentType enum ComponentType { COMPONENT, COMPONENT2D, TEST, PRINTTREEONCE, SPRITE2D, ROTATION, TIMER, GUICOMPONENT } // 3.9.2. Component class Component { // almost abstract ! String name; ComponentType componentType; Component(String name) { this.name=name; componentType=ComponentType.COMPONENT; } void ready(Node n) { } void process(Node n, float delta) { } void signal(Node n, PEvent pEvent) { } void input(Node n, PEvent pEvent) { } void display(Node n) { } } // 3.9.3. Component2D class Component2D { // almost abstract ! String name; ComponentType componentType; Component2D(String name) { this.name=name; componentType=ComponentType.COMPONENT2D; } void ready(Node2D n) { } void process(Node2D n, float delta) { } void signal(Node2D n, PEvent pEvent) { } void input(Node2D n, PEvent pEvent) { } void display(Node2D n) { } } // 3.9.4. Sprite2D class Sprite2D extends Component2D { PImage image; Sprite2D(String name, PImage image) { super(name); componentType=ComponentType.SPRITE2D; this.image=image; } void ready(Node2D n) { n.size.set(image.width, image.height); } void display(Node2D n) { image(image, 0, 0); if (pengine.debug) { push(); rectMode(CENTER); stroke(255, 0, 0); noFill(); rect(0, 0, n.size.x, n.size.y); fill(255, 0, 0); circle(0, 0, 20); pop(); } } } // 3.9.5. Rotation class Rotation extends Component2D { float rotation; Rotation(String name, float rotation) { super(name); componentType=ComponentType.ROTATION; this.rotation=rotation; } void process(Node2D n, float delta) { n.rotation+=rotation*delta; } } // 3.9.6. Timer class Timer extends Component2D { float time; // seconds float _start; // seconds boolean isOn; int loop; // loop number (infinite if 0) int _loopCount; Timer(String name, float time) { super(name); componentType=ComponentType.TIMER; this.time=time; isOn=false; loop=0; _loopCount=0; } Timer setOn() { isOn=true; if (frameCount!=0) { // not called in setup() ! _start=millis()/1000; _loopCount=0; } return this; } Timer setLoop(int loop) { this.loop=loop; return this; } void ready(Node2D n) { if (isOn) { _start=millis()/1000; } } void process(Node2D n, float delta) { if (isOn && (millis()/1000) >= (_start+time) ) { n.signal(new PEvent(EventType.TIMER, n)); _loopCount+=1; if (loop==0 || _loopCount<=loop) { _start=millis()/1000; // start Timer again } else { isOn=false; } } } //void signal(Node2D n, PEvent pEvent) { // if (pEvent.eventType==EventType.TIMER && pEvent.source==this) { // // DO something // } //} } // 3.9.7. Background // 4. Physic Engine // 5. GUI // 5.1. Control class Control extends Node2D { GUIcomponent themeComponent; Control(String name) { super(name); nodeType=NodeType.CONTROL; } void ready() { pengine.theme.listen(this); themeComponent=pengine.theme.getGUIcomponent(this); super.ready(); } void display() { super.display(); if (themeComponent.themeType!=ThemeType.NULL) { themeComponent.display(this); } else { if (pengine.debug) { bugMessage("themeScript.themeType is NULL in "+name+" ("+nodeType+") !"); } } } void signal(PEvent pEvent) { super.signal(pEvent); if (pEvent.eventType==EventType.THEME) { themeComponent=pengine.theme.getGUIcomponent(this); } } } // 5.2. GUIcomponent class GUIcomponent extends Component2D { ThemeType themeType; NodeType nodeType; GUIcomponent(String name) { super(name); componentType=ComponentType.GUICOMPONENT; themeType=ThemeType.NULL; nodeType=NodeType.NULL; } } // 5.3. Themes // 5.3.1. ThemeType enum ThemeType { NULL, BASIC, FLAT, SF } // 5.3.2. Theme class Theme implements Observable { // Pour pouvoir indiquer aux widgets un changement de thème : ArrayList widgets = new ArrayList(); // La "réserve de scripts" : ArrayList components = new ArrayList<>(); ThemeType currentTheme; Theme() { currentTheme=ThemeType.FLAT; } // implements Observable void listen(Node node) { // Les widgets écoutent pour être tenu au courant d'un changement de thème widgets.add(node); } void notify(PEvent event) {// Indiquer un changement de thème for (Node node : widgets) { node.signal(event); } } // End implements Observable void addGUIcomponent(GUIcomponent component) { components.add(component); } GUIcomponent getGUIcomponent(Control widget) { GUIcomponent toReturn = new GUIcomponent("a GUI component"); for (GUIcomponent component : components) { if (component.nodeType==widget.nodeType) { if (component.themeType==currentTheme) { return component; } else { if (component.themeType==ThemeType.BASIC) { toReturn=component; } } } } return toReturn; } void setTheme(ThemeType themeType) { currentTheme=themeType; notify(new PEvent(EventType.THEME, this)); } } // End Theme // 5.4. Focus // 5.5. Languages // 5.W. Widgets // 5.W.1. Slider // 5.W.1.1. Slider class class Slider extends Control { float percent=0; Slider(String name) { super(name); nodeType=NodeType.SLIDER; } Slider setSize(float w, float h) { return (Slider) super.setSize(w, h); } Slider setPercent(float percent) { this.percent=percent; return this; } Slider setPosition(float x, float y) { super.setPosition(x, y); return this; } } // 5.W.1.2. Slider GUIscript for FLAT class SliderBASIC extends GUIcomponent { SliderBASIC(String name) { super(name); themeType=ThemeType.BASIC; nodeType=NodeType.SLIDER; } void display(Node2D n) { push(); stroke(200); strokeWeight(5); noFill(); rectMode(CENTER); Slider s = (Slider) n; rect(0, 0, s.size.x, s.size.y); noStroke(); fill(0); if (s.percent!=0) { { rectMode(CORNER); rect(s.topLeft().x, s.topLeft().y, s.size.x*s.percent, s.size.y); } } pop(); } } // 6. special // 6.1. Preload // Must be declared after Node2D class in JS ! PImage pLoadImage(String file) { PImage image=loadImage(file); pengine.preload.addImage(image); return image; } /** For PengineJS compatibility */ PFont pLoadFont(String file) { return loadFont(file); } class Preload extends Node2D { ArrayList images = new ArrayList<>(); Boolean done= false; Boolean wait = false; float MAXTIME = 1.5; int filesLoaded=0; int filesToLoad=0; Slider slider=new Slider("Preload Slider") .setPercent(0) .setPosition(0, 100) .setSize(150, 30); Preload() { super("preload"); nodeType = NodeType.PRELOAD; } void addImage(PImage image) { images.add(image); } void ready() { addComponent(new Sprite2D("logo PEngine", loadImage("PEngine.png"))); addChild(slider); Node2D star = new Node2D("Star").setPosition(50, 50) .addComponent(new Sprite2D("logo Star", loadImage("logoP5.png"))) .addComponent(new Rotation("star rotation", 4)); star.scale=.4; addChild(star); filesToLoad=images.size(); } void process(float delta) { if (!wait) { filesLoaded=0; for (int i=0; iMAXTIME) { done=true; } } if (done) { pengine.setScene(pengine.preloadPreviousScene); pengine.preloadPreviousScene=null; pengine.preload=null; } } void display() { super.display(); } }