├── .idea ├── .name ├── scopes │ ├── scope_settings.xml │ └── Original_Code.xml ├── encodings.xml ├── copyright │ ├── profiles_settings.xml │ └── Apache.xml ├── vcs.xml ├── inspectionProfiles │ ├── profiles_settings.xml │ └── Project_Default.xml ├── misc.xml ├── codeStyleSettings.xml ├── modules.xml ├── gradle.xml ├── compiler.xml └── uiDesigner.xml ├── demos-core ├── build.gradle ├── src │ └── com │ │ └── matthewmichelotti │ │ └── collider │ │ └── demos │ │ ├── MousePosListener.java │ │ ├── Game.java │ │ ├── Geom.java │ │ ├── PosAndVel.java │ │ ├── FunctionEvent.java │ │ ├── comps │ │ ├── CText.java │ │ ├── CBounds.java │ │ ├── CMorphEnemyShip.java │ │ ├── CTarget.java │ │ ├── CEnemyShip.java │ │ ├── CIndicator.java │ │ ├── CMorphStickySpinner.java │ │ ├── CCircFade.java │ │ ├── CStickyGun.java │ │ ├── CBullet.java │ │ ├── CVarietyGun.java │ │ ├── CSticky.java │ │ ├── CMorphBullet.java │ │ ├── CMorphSticky.java │ │ ├── CPlayerShip.java │ │ ├── CWaveBullet.java │ │ ├── CCoagParticle.java │ │ └── CElastic.java │ │ ├── WaveUpdater.java │ │ ├── Component.java │ │ ├── Demos.java │ │ ├── Scenarios.java │ │ └── GameEngine.java └── demos-core.iml ├── gradle.properties ├── settings.gradle ├── .gitignore ├── demos-desktop ├── build.gradle ├── src │ └── com │ │ └── matthewmichelotti │ │ └── collider │ │ └── demos │ │ └── Main.java └── demos-desktop.iml ├── core ├── src │ └── com │ │ └── matthewmichelotti │ │ └── collider │ │ ├── Int2DIterator.java │ │ ├── FunctionEvent.java │ │ ├── Dir.java │ │ ├── ECollide.java │ │ ├── util │ │ ├── ColliderListener.java │ │ ├── ContProcess.java │ │ ├── ColliderProcess.java │ │ └── ContProcesses.java │ │ ├── Normal.java │ │ ├── EReiterate.java │ │ ├── Arith.java │ │ ├── ColliderOpts.java │ │ ├── InteractTester.java │ │ ├── ColliderEvent.java │ │ ├── IntBox.java │ │ ├── HBCircle.java │ │ ├── HBPositioned.java │ │ ├── HBRect.java │ │ ├── Field.java │ │ ├── SetPool.java │ │ ├── HitBox.java │ │ └── CollisionTester.java ├── core.iml ├── build.gradle └── src_gdx_util │ └── com │ └── matthewmichelotti │ └── collider │ ├── ArrayReflection.java │ ├── Pool.java │ └── RandomXS128.java ├── collider.iml ├── README.md └── LICENSE /.idea/.name: -------------------------------------------------------------------------------- 1 | collider -------------------------------------------------------------------------------- /demos-core/build.gradle: -------------------------------------------------------------------------------- 1 | sourceSets.main.java.srcDirs = [ "src/" ] 2 | -------------------------------------------------------------------------------- /gradle.properties: -------------------------------------------------------------------------------- 1 | org.gradle.daemon=true 2 | org.gradle.configureondemand=true -------------------------------------------------------------------------------- /settings.gradle: -------------------------------------------------------------------------------- 1 | rootProject.name = 'collider' 2 | include 'core' 3 | include 'demos-core' 4 | include 'demos-desktop' 5 | 6 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | /*/build 3 | /gradlew 4 | /gradlew.bat 5 | /gradle 6 | /.idea/workspace.xml 7 | /.idea/tasks.xml 8 | .gradle 9 | .idea/libraries/Gradle__*.xml -------------------------------------------------------------------------------- /.idea/scopes/scope_settings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | -------------------------------------------------------------------------------- /.idea/encodings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /.idea/scopes/Original_Code.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /.idea/copyright/profiles_settings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /.idea/vcs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /.idea/inspectionProfiles/profiles_settings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 7 | -------------------------------------------------------------------------------- /demos-desktop/build.gradle: -------------------------------------------------------------------------------- 1 | sourceSets.main.java.srcDirs = [ "src/" ] 2 | 3 | project.ext.mainClassName = "com.matthewmichelotti.collider.demos.Main" 4 | 5 | task run(dependsOn: classes, type: JavaExec) { 6 | main = project.mainClassName 7 | classpath = sourceSets.main.runtimeClasspath 8 | standardInput = System.in 9 | ignoreExitValue = true 10 | } 11 | -------------------------------------------------------------------------------- /.idea/misc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /.idea/codeStyleSettings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 11 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /.idea/modules.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /.idea/gradle.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /.idea/compiler.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /core/src/com/matthewmichelotti/collider/Int2DIterator.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2013-2014 Matthew D. Michelotti 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.matthewmichelotti.collider; 18 | 19 | interface Int2DIterator { 20 | boolean isDone(); 21 | void next(); 22 | int getX(); 23 | int getY(); 24 | } 25 | -------------------------------------------------------------------------------- /.idea/copyright/Apache.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 9 | -------------------------------------------------------------------------------- /demos-core/src/com/matthewmichelotti/collider/demos/MousePosListener.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2013-2014 Matthew D. Michelotti 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.matthewmichelotti.collider.demos; 18 | 19 | /** 20 | * An interface for listening for mouse positions. 21 | * @author Matthew Michelotti 22 | */ 23 | public interface MousePosListener { 24 | public void updateMousePos(double endTime, double mouseX, double mouseY); 25 | } 26 | -------------------------------------------------------------------------------- /demos-core/src/com/matthewmichelotti/collider/demos/Game.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2013-2014 Matthew D. Michelotti 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.matthewmichelotti.collider.demos; 18 | 19 | import java.util.Random; 20 | 21 | /** 22 | * Contains some static fields used by the game. 23 | * @author Matthew Michelotti 24 | */ 25 | public class Game { 26 | public final static GameEngine engine = new GameEngine(); 27 | public final static Random rand = new Random(); 28 | 29 | private Game() {} 30 | } 31 | -------------------------------------------------------------------------------- /core/src/com/matthewmichelotti/collider/FunctionEvent.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2013-2014 Matthew D. Michelotti 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.matthewmichelotti.collider; 18 | 19 | abstract class FunctionEvent implements Comparable { 20 | double time; 21 | int id; 22 | 23 | FunctionEvent() {} 24 | 25 | abstract void resolve(Collider collider); 26 | 27 | @Override public final int compareTo(FunctionEvent o) { 28 | if(time > o.time) return 1; 29 | if(time == o.time) return id - o.id; 30 | return -1; 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /demos-desktop/src/com/matthewmichelotti/collider/demos/Main.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2013-2014 Matthew D. Michelotti 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.matthewmichelotti.collider.demos; 18 | 19 | import com.badlogic.gdx.backends.lwjgl.LwjglApplication; 20 | import com.badlogic.gdx.backends.lwjgl.LwjglApplicationConfiguration; 21 | 22 | public class Main { 23 | public static void main(String[] args) { 24 | LwjglApplicationConfiguration cfg = new LwjglApplicationConfiguration(); 25 | cfg.title = "collider-demos"; 26 | cfg.width = 1280; 27 | cfg.height = 720; 28 | 29 | new LwjglApplication(new Demos(), cfg); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /core/core.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /demos-core/demos-core.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /collider.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /core/src/com/matthewmichelotti/collider/Dir.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2013-2014 Matthew D. Michelotti 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.matthewmichelotti.collider; 18 | 19 | final class Dir { 20 | final static int R = 0, U = 1, L = 2, D = 3; 21 | 22 | static int opp(int dir) { 23 | return (dir + 2) & 3; 24 | } 25 | 26 | static int dot(int dirA, int dirB) { 27 | int relDir = (dirB - dirA) & 3; 28 | switch(relDir) { 29 | case 0: return 1; 30 | case 2: return -1; 31 | default: return 0; 32 | } 33 | } 34 | 35 | static int x(int dir) { 36 | switch(dir) { 37 | case R: return 1; 38 | case L: return -1; 39 | default: return 0; 40 | } 41 | } 42 | 43 | static int y(int dir) { 44 | switch(dir) { 45 | case U: return 1; 46 | case D: return -1; 47 | default: return 0; 48 | } 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /core/src/com/matthewmichelotti/collider/ECollide.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2013-2014 Matthew D. Michelotti 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.matthewmichelotti.collider; 18 | 19 | final class ECollide extends FunctionEvent { 20 | private HitBox a, b; 21 | private int idA, idB; 22 | private boolean collided; 23 | 24 | ECollide() {} 25 | 26 | void init(HitBox a, HitBox b, double time, boolean collided) { 27 | this.a = a; 28 | this.b = b; 29 | this.idA = a.getChangeId(); 30 | this.idB = b.getChangeId(); 31 | this.time = time; 32 | this.collided = collided; 33 | } 34 | 35 | @Override 36 | void resolve(Collider collider) { 37 | if(a.getChangeId() == idA && b.getChangeId() == idB) { 38 | collider.setCollision(a, b, collided); 39 | } 40 | a = null; 41 | b = null; 42 | collider.freeEvent(this); 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /demos-core/src/com/matthewmichelotti/collider/demos/Geom.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2013-2014 Matthew D. Michelotti 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.matthewmichelotti.collider.demos; 18 | 19 | import com.matthewmichelotti.collider.HBCircle; 20 | import com.matthewmichelotti.collider.HBRect; 21 | import com.matthewmichelotti.collider.HitBox; 22 | 23 | /** 24 | * Some geometric helper functions. 25 | * @author Matthew Michelotti 26 | */ 27 | public class Geom { 28 | 29 | private Geom() {} 30 | 31 | public static double area(HitBox hitBox) { 32 | if(hitBox instanceof HBCircle) { 33 | double r = .5*((HBCircle)hitBox).getDiam(); 34 | return Math.PI*r*r; 35 | } 36 | else { 37 | HBRect rect = (HBRect)hitBox; 38 | return rect.getWidth()*rect.getHeight(); 39 | } 40 | } 41 | 42 | public static double area2Diam(double area) { 43 | return 2*Math.sqrt(area/Math.PI); 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /core/src/com/matthewmichelotti/collider/util/ColliderListener.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2013-2014 Matthew D. Michelotti 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.matthewmichelotti.collider.util; 18 | 19 | import com.matthewmichelotti.collider.ColliderEvent; 20 | 21 | /** 22 | * An Event Listener interface for handling events generated by 23 | * a {@link com.matthewmichelotti.collider.Collider}. 24 | * 25 | * @see ColliderProcess 26 | * @author Matthew Michelotti 27 | */ 28 | public interface ColliderListener { 29 | 30 | /** 31 | * Handle a collision event, called when two interactable HitBoxes 32 | * collide. 33 | * @param evt The collision event. 34 | */ 35 | public void collision(ColliderEvent evt); 36 | 37 | /** 38 | * Handle a separation event, called when two interactable HitBoxes 39 | * separate. 40 | * @param evt The separation event. 41 | */ 42 | public void separation(ColliderEvent evt); 43 | } 44 | -------------------------------------------------------------------------------- /demos-core/src/com/matthewmichelotti/collider/demos/PosAndVel.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2013-2014 Matthew D. Michelotti 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.matthewmichelotti.collider.demos; 18 | 19 | /** 20 | * Helper class to store 2D coordinate and velocity information. 21 | * @author Matthew Michelotti 22 | */ 23 | public class PosAndVel { 24 | public double x, y, vx, vy; 25 | 26 | public PosAndVel() {} 27 | 28 | public PosAndVel(double x, double y, double vx, double vy) { 29 | this.x = x; 30 | this.y = y; 31 | this.vx = vx; 32 | this.vy = vy; 33 | } 34 | 35 | public static PosAndVel radial(double baseX, double baseY, double angle, 36 | double offset, double vel) 37 | { 38 | PosAndVel pos = new PosAndVel(); 39 | double cos = Math.cos(angle); 40 | double sin = Math.sin(angle); 41 | pos.x = baseX + cos*offset; 42 | pos.y = baseY + sin*offset; 43 | pos.vx = cos*vel; 44 | pos.vy = sin*vel; 45 | return pos; 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /core/build.gradle: -------------------------------------------------------------------------------- 1 | sourceSets.main.java.srcDirs = [ "src/", "src_gdx_util/" ] 2 | 3 | //TODO upload Jars to maven... 4 | 5 | ext { 6 | projectBaseName = "collider" 7 | } 8 | 9 | jar { 10 | baseName = projectBaseName 11 | } 12 | 13 | task apiDocs(type: Javadoc) { 14 | source = sourceSets.main.allJava 15 | //options.addStringOption "bottom", "Copyright © 2013-2014 Matthew D. Michelotti" 16 | options.setWindowTitle "Collider $version API" 17 | options.setBottom "Copyright © 2013-2014 Matthew D. Michelotti" 18 | options.setDocTitle "Collider $version API" 19 | } 20 | 21 | task sourcesJar(type: Jar, dependsOn: classes) { 22 | classifier = 'sources' 23 | from sourceSets.main.allSource 24 | baseName = projectBaseName 25 | } 26 | 27 | task javadocJar(type: Jar, dependsOn: apiDocs) { 28 | classifier = 'javadoc' 29 | from apiDocs.destinationDir 30 | baseName = projectBaseName 31 | } 32 | 33 | task distAsZip(type: Zip, dependsOn: [jar, apiDocs, sourcesJar]) { 34 | from sourcesJar.archivePath, jar.archivePath, "../LICENSE", "../README.md" 35 | from(apiDocs.destinationDir) { 36 | into "doc" 37 | } 38 | rename "README\\.md", "README.txt" 39 | baseName = projectBaseName 40 | } 41 | 42 | 43 | //task binaryJar(type: Jar) { 44 | // baseName = "collider" 45 | // //from files(sourceSets.main.output.classesDir) 46 | // //from files(sourceSets.main.output.resourcesDir) 47 | // with jar 48 | //} 49 | 50 | //distributions { 51 | // main { 52 | // baseName = projectBaseName 53 | // contents { 54 | // from apiDocs.destinationDir, sourcesJar.archivePath, jar.archivePath 55 | // } 56 | // } 57 | //} 58 | -------------------------------------------------------------------------------- /demos-core/src/com/matthewmichelotti/collider/demos/FunctionEvent.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2013-2014 Matthew D. Michelotti 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.matthewmichelotti.collider.demos; 18 | 19 | /** 20 | * An event resolved by the {@link com.matthewmichelotti.collider.demos.GameEngine} at a specified time. 21 | * @author Matthew Michelotti 22 | */ 23 | public abstract class FunctionEvent implements Comparable { 24 | private static int NEXT_ID = 0; 25 | 26 | private double time; 27 | private int id; 28 | 29 | public FunctionEvent() { 30 | setTime(Game.engine.getTime()); 31 | } 32 | 33 | public FunctionEvent(double time) { 34 | setTime(time); 35 | } 36 | 37 | public void setTime(double time) { 38 | this.time = time; 39 | this.id = NEXT_ID++; 40 | } 41 | 42 | public double getTime() { 43 | return time; 44 | } 45 | 46 | public abstract void resolve(); 47 | 48 | @Override public final int compareTo(FunctionEvent o) { 49 | if(time > o.time) return 1; 50 | if(time == o.time) return id - o.id; 51 | return -1; 52 | } 53 | } -------------------------------------------------------------------------------- /core/src/com/matthewmichelotti/collider/Normal.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2013-2014 Matthew D. Michelotti 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.matthewmichelotti.collider; 18 | 19 | /** 20 | * A normal vector between two HitBoxes. 21 | * 22 | * @author Matthew Michelotti 23 | * @see HitBox#getNormal(HitBox) 24 | */ 25 | public final class Normal { 26 | double x, y, overlap; 27 | 28 | Normal() {} 29 | 30 | /** 31 | * Returns the x-coordinate of the unit normal vector. 32 | * @return The x-coordinate of the unit normal vector. 33 | */ 34 | public double getUnitX() {return x;} 35 | 36 | /** 37 | * Returns the y-coordinate of the unit normal vector. 38 | * @return The y-coordinate of the unit normal vector. 39 | */ 40 | public double getUnitY() {return y;} 41 | 42 | /** 43 | * Returns the magnitude of the normal vector. 44 | * This is the amount that the two HitBoxes overlap. 45 | * A negative value represents the distance between 46 | * two non-overlapping HitBoxes. 47 | * @return The magnitude of the normal vector. 48 | */ 49 | public double getOverlap() {return overlap;} 50 | } 51 | -------------------------------------------------------------------------------- /core/src/com/matthewmichelotti/collider/util/ContProcess.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2013-2014 Matthew D. Michelotti 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.matthewmichelotti.collider.util; 18 | 19 | /** 20 | * A continuous-time process. This can be used to couple Collider 21 | * with other continuous-time processes that the user creates. 22 | * 23 | * @see ContProcesses 24 | * @author Matthew Michelotti 25 | */ 26 | public interface ContProcess { 27 | /** 28 | * Returns the time that the next event will occur in this process. 29 | * Assumes no other processes have an event before that time. 30 | * @return The time of the next event. 31 | */ 32 | public double peekNextEventTime(); 33 | 34 | /** 35 | * Advances this process to the given time, without resolving 36 | * any events at that time. No events should occur before 37 | * this time either. 38 | * @param time Time to advance the process to. This must be 39 | * at most {@link #peekNextEventTime()}. 40 | */ 41 | public void stepToTime(double time); 42 | 43 | /** 44 | * Resolve an event that occurs at the current time. 45 | */ 46 | public void resolveEvent(); 47 | } 48 | -------------------------------------------------------------------------------- /core/src/com/matthewmichelotti/collider/EReiterate.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2013-2014 Matthew D. Michelotti 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.matthewmichelotti.collider; 18 | 19 | final class EReiterate extends FunctionEvent { 20 | private HitBox hitBox; 21 | private double period; 22 | private double endTime; 23 | private int changeId; 24 | 25 | EReiterate() {} 26 | 27 | void init(HitBox hitBox, double startTime, double endTime, double period) { 28 | if(startTime >= endTime) throw new RuntimeException(); 29 | this.hitBox = hitBox; 30 | this.time = startTime; 31 | this.endTime = endTime; 32 | this.period = period; 33 | this.changeId = hitBox.getChangeId(); 34 | } 35 | 36 | @Override 37 | void resolve(Collider collider) { 38 | if(changeId == hitBox.getChangeId()) { 39 | double stepEndTime = collider.getTime() + period; 40 | if(endTime <= stepEndTime) { 41 | hitBox.commit(endTime); 42 | } 43 | else { 44 | hitBox.commit(stepEndTime); 45 | collider.processCurHBAndCollision(false); 46 | changeId = hitBox.getChangeId(); 47 | time = stepEndTime; 48 | collider.queue(this); 49 | return; 50 | } 51 | } 52 | hitBox = null; 53 | collider.freeEvent(this); 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /demos-core/src/com/matthewmichelotti/collider/demos/comps/CText.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2013-2014 Matthew D. Michelotti 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.matthewmichelotti.collider.demos.comps; 18 | 19 | import com.badlogic.gdx.graphics.Color; 20 | import com.matthewmichelotti.collider.HBRect; 21 | import com.matthewmichelotti.collider.demos.Component; 22 | import com.matthewmichelotti.collider.demos.Game; 23 | 24 | /** 25 | * Displays a text message. 26 | * @author Matthew Michelotti 27 | */ 28 | public class CText extends Component { 29 | private String message; 30 | 31 | public CText(String message, double x, double y) { 32 | super(Game.engine.makeRect()); 33 | HBRect rect = rect(); 34 | rect.setPos(x, y); 35 | rect.setDims(1); 36 | rect.setGroup(-1); 37 | rect.commit(Double.POSITIVE_INFINITY); 38 | this.message = message; 39 | } 40 | 41 | @Override public void onCollide(Component other) {} 42 | @Override public void onSeparate(Component other) {} 43 | @Override public boolean canInteract(Component other) {return false;} 44 | @Override public boolean interactsWithBullets() {return false;} 45 | @Override public Color getColor() {return null;} 46 | @Override public String getMessage() {return message;} 47 | } 48 | -------------------------------------------------------------------------------- /core/src/com/matthewmichelotti/collider/Arith.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2013-2014 Matthew D. Michelotti 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.matthewmichelotti.collider; 18 | 19 | final class Arith { 20 | static double quadRootAscending(double a, double b, double c) { 21 | double det = b*b - 4*a*c; 22 | if(det < 0.0) return Double.NaN; 23 | if(b >= 0) return (2*c)/(-b - Math.sqrt(det)); 24 | else return (-b + Math.sqrt(det))/(2*a); 25 | } 26 | 27 | static int floor(double value) { 28 | int result = (int)value; 29 | if(value < 0.0 && value != result) return result - 1; 30 | else return result; 31 | } 32 | 33 | static int ceil(double value) { 34 | int result = (int)value; 35 | if(value > 0.0 && value != result) return result + 1; 36 | else return result; 37 | } 38 | 39 | static double min(double a, double b) { 40 | return (a < b) ? a : b; 41 | } 42 | 43 | static double max(double a, double b) { 44 | return (a > b) ? a : b; 45 | } 46 | 47 | static int min(int a, int b) { 48 | return (a < b) ? a : b; 49 | } 50 | 51 | static int max(int a, int b) { 52 | return (a > b) ? a : b; 53 | } 54 | 55 | static double abs(double value) { 56 | if(value < 0.0) return -value; 57 | else return value; 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /demos-core/src/com/matthewmichelotti/collider/demos/comps/CBounds.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2013-2014 Matthew D. Michelotti 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.matthewmichelotti.collider.demos.comps; 18 | 19 | import com.badlogic.gdx.graphics.Color; 20 | import com.matthewmichelotti.collider.HBRect; 21 | import com.matthewmichelotti.collider.demos.Component; 22 | import com.matthewmichelotti.collider.demos.Game; 23 | import com.matthewmichelotti.collider.demos.GameEngine; 24 | 25 | /** 26 | * A box that extends slightly past the bounds of the screen. 27 | * @author Matthew Michelotti 28 | */ 29 | public class CBounds extends Component { 30 | public final static int PAD = 80; 31 | 32 | public CBounds() { 33 | super(Game.engine.makeRect()); 34 | HBRect rect = rect(); 35 | rect.setPos(.5*GameEngine.SCREEN_WIDTH, .5*GameEngine.SCREEN_HEIGHT); 36 | rect.setDims(GameEngine.SCREEN_WIDTH + 2*PAD, GameEngine.SCREEN_HEIGHT + 2*PAD); 37 | rect.commit(Double.POSITIVE_INFINITY); 38 | } 39 | 40 | @Override public void onCollide(Component other) {} 41 | @Override public void onSeparate(Component other) {} 42 | @Override public boolean canInteract(Component other) {return false;} 43 | @Override public boolean interactsWithBullets() {return true;} 44 | @Override public Color getColor() {return null;} 45 | } 46 | -------------------------------------------------------------------------------- /demos-core/src/com/matthewmichelotti/collider/demos/comps/CMorphEnemyShip.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2013-2014 Matthew D. Michelotti 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.matthewmichelotti.collider.demos.comps; 18 | 19 | import com.matthewmichelotti.collider.HBCircle; 20 | import com.matthewmichelotti.collider.demos.FunctionEvent; 21 | import com.matthewmichelotti.collider.demos.Game; 22 | import com.matthewmichelotti.collider.demos.PosAndVel; 23 | 24 | /** 25 | * An enemy ship for a danmaku scenario that uses a radial attack pattern 26 | * with morphing bullets. 27 | * @author Matthew Michelotti 28 | */ 29 | public class CMorphEnemyShip extends CTarget { 30 | private double baseAngle = .1; 31 | 32 | public CMorphEnemyShip(final double x, final double y) { 33 | super(Game.engine.makeCircle(), CEnemyShip.COLOR); 34 | HBCircle circ = circ(); 35 | circ.setDiam(CEnemyShip.DIAM); 36 | circ.setPos(x, y); 37 | circ.commit(Double.POSITIVE_INFINITY); 38 | 39 | final int numPoints = 30; 40 | final double offset = .5*(CEnemyShip.DIAM + CMorphBullet.DIAM); 41 | 42 | Game.engine.addEvent(new FunctionEvent() { 43 | @Override public void resolve() { 44 | for(int i = 0; i < numPoints; i++) { 45 | if(i % 10 < 6) continue; 46 | double angle = baseAngle + 2*Math.PI*i/(double)numPoints; 47 | new CMorphBullet(PosAndVel.radial(x, y, angle, offset, 100)); 48 | } 49 | baseAngle = (baseAngle + .24) % (2*Math.PI); 50 | setTime(getTime() + .9); 51 | Game.engine.addEvent(this); 52 | } 53 | }); 54 | } 55 | 56 | @Override public boolean isEnemy() {return true;} 57 | } 58 | -------------------------------------------------------------------------------- /demos-core/src/com/matthewmichelotti/collider/demos/comps/CTarget.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2013-2014 Matthew D. Michelotti 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.matthewmichelotti.collider.demos.comps; 18 | 19 | import com.badlogic.gdx.graphics.Color; 20 | import com.matthewmichelotti.collider.HBPositioned; 21 | import com.matthewmichelotti.collider.demos.Component; 22 | import com.matthewmichelotti.collider.demos.Game; 23 | 24 | /** 25 | * A component that flashes when hit by a bullet, 26 | * sticky circle, etc. 27 | * @author Matthew Michelotti 28 | */ 29 | public class CTarget extends Component { 30 | private final static double LIT_TIME = .17; 31 | private double hitTime = Double.NEGATIVE_INFINITY; 32 | private Color color; 33 | private Color blendColor = new Color(); 34 | 35 | public CTarget(HBPositioned hitBox, Color color) { 36 | super(hitBox); 37 | this.color = color; 38 | } 39 | 40 | public boolean isEnemy() {return false;} 41 | public void hit() {hitTime = Game.engine.getTime();} 42 | 43 | @Override public void onCollide(Component other) {} 44 | @Override public void onSeparate(Component other) {} 45 | @Override public boolean canInteract(Component other) {return false;} 46 | @Override public boolean interactsWithBullets() {return true;} 47 | 48 | @Override 49 | public Color getColor() { 50 | double timeDiff = Game.engine.getTime() - hitTime; 51 | if(timeDiff >= LIT_TIME) return color; 52 | double alpha = Math.cos(.5*Math.PI*timeDiff/LIT_TIME); 53 | if(alpha <= 0.0) return color; 54 | blendColor.set(color); 55 | return blendColor.lerp(Color.WHITE, (float)alpha); 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Collider 2 | 3 | Collider is a Java library for efficient and precise 2-D collision 4 | detection with a simple interface. Collider uses [continuous collision 5 | detection](http://en.wikipedia.org/wiki/Collision_detection#A_posteriori_.28discrete.29_versus_a_priori_.28continuous.29), 6 | which basically means that the time of the collision is determined very 7 | precisely as opposed to using a time-stepping method. 8 | 9 | ### On Hiatus 10 | 11 | The Java Collider project is on hiatus. 12 | I have shifted my focus to the [Rust](https://www.rust-lang.org/) programming language, 13 | so I have ported Collider to Rust. 14 | That project is more up-to-date, and you can find it at https://github.com/SergiusIW/collider-rs. 15 | 16 | ### Download 17 | 18 | Release builds of Collider are available at 19 | https://github.com/SergiusIW/collider/releases. 20 | At some point in the future I will start uploading release builds to Maven Central. 21 | 22 | ### Documentation 23 | 24 | Javadocs for Collider come with the distribution that you download. The 25 | Javadocs for the latest release may be read online at 26 | http://www.matthewmichelotti.com/projects/collider/api/. 27 | 28 | ### Source Code 29 | 30 | This project is open-source and available on GitHub. 31 | You can find the source code at https://github.com/SergiusIW/collider. 32 | 33 | ### Demos 34 | 35 | There is currently no tutorial for using the Collider library. Along 36 | with reading the javadocs, you can learn how to use Collider by studying 37 | the code for the demos. These demos were used to make the [youtube 38 | video](http://www.youtube.com/watch?v=sFNw-wYebOc). The code for the 39 | demos can be found in two modules in the [github 40 | repository](https://github.com/SergiusIW/collider), demos-core 41 | and demos-desktop. 42 | 43 | ### Dependencies 44 | 45 | The Collider library does not depend on any third party libraries. 46 | However, the Collider demos depend on [LibGDX](http://libgdx.badlogicgames.com/). 47 | Building with Gradle will download these dependencies for you. 48 | See the build.gradle files for more details. 49 | 50 | ### License 51 | 52 | Collider is licensed under the [Apache 2.0 53 | License](http://www.apache.org/licenses/LICENSE-2.0.html). 54 | -------------------------------------------------------------------------------- /demos-core/src/com/matthewmichelotti/collider/demos/comps/CEnemyShip.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2013-2014 Matthew D. Michelotti 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.matthewmichelotti.collider.demos.comps; 18 | 19 | import com.badlogic.gdx.graphics.Color; 20 | import com.matthewmichelotti.collider.HBCircle; 21 | import com.matthewmichelotti.collider.demos.FunctionEvent; 22 | import com.matthewmichelotti.collider.demos.Game; 23 | import com.matthewmichelotti.collider.demos.PosAndVel; 24 | 25 | /** 26 | * An enemy ship for a danmaku scenario that uses a radial attack pattern. 27 | * @author Matthew Michelotti 28 | */ 29 | public class CEnemyShip extends CTarget { 30 | public final static double DIAM = 25; 31 | 32 | public final static Color COLOR = new Color(1.0f, 0.25f, 0.07f, 1.0f); 33 | private double baseAngle = .1; 34 | 35 | public CEnemyShip(final double x, final double y) { 36 | super(Game.engine.makeCircle(), COLOR); 37 | HBCircle circ = circ(); 38 | circ.setDiam(DIAM); 39 | circ.setPos(x, y); 40 | circ.commit(Double.POSITIVE_INFINITY); 41 | 42 | final int numPoints = 170; 43 | final double offset = .5*(DIAM + CBullet.DIAM); 44 | 45 | Game.engine.addEvent(new FunctionEvent() { 46 | @Override public void resolve() { 47 | for(int i = 0; i < numPoints; i++) { 48 | if(i % 10 < 6) continue; 49 | double angle = baseAngle + 2*Math.PI*i/(double)numPoints; 50 | new CBullet(true, PosAndVel.radial(x, y, angle, offset, 100)); 51 | } 52 | baseAngle = (baseAngle + .24) % (2*Math.PI); 53 | setTime(getTime() + .9); 54 | Game.engine.addEvent(this); 55 | } 56 | }); 57 | } 58 | 59 | @Override public boolean isEnemy() {return true;} 60 | } 61 | -------------------------------------------------------------------------------- /demos-core/src/com/matthewmichelotti/collider/demos/comps/CIndicator.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2013-2014 Matthew D. Michelotti 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.matthewmichelotti.collider.demos.comps; 18 | 19 | import com.badlogic.gdx.graphics.Color; 20 | import com.matthewmichelotti.collider.demos.Component; 21 | import com.matthewmichelotti.collider.demos.Game; 22 | 23 | /** 24 | * Indicates how many bullets are overlapping with this Component. 25 | * @author Matthew Michelotti 26 | */ 27 | public class CIndicator extends Component { 28 | private static Color LO_COLOR = new Color(0.0f, 0.0f, 0.3f, 1.0f); 29 | private static Color HI_COLOR = new Color(0.2f, 0.8f, 1.0f, 1.0f); 30 | 31 | private int overlaps = 0; 32 | private Color blendColor = new Color(); 33 | 34 | public CIndicator(double x, double y, boolean isRect) { 35 | super(isRect ? Game.engine.makeRect() : Game.engine.makeCircle()); 36 | if(isRect) rect().setDims(160.0); 37 | else circ().setDiam(180.0); 38 | hitBox().setPos(x, y); 39 | hitBox().commit(Double.POSITIVE_INFINITY); 40 | } 41 | 42 | @Override public void onCollide(Component other) {overlaps++;} 43 | @Override public void onSeparate(Component other) {overlaps--;} 44 | @Override public boolean canInteract(Component other) { 45 | return other instanceof CBullet || other instanceof CMorphBullet 46 | || other instanceof CWaveBullet; 47 | } 48 | @Override public boolean interactsWithBullets() {return true;} 49 | @Override public Color getColor() { 50 | float alpha = 1.0f - (1.0f/(1 + .2f*overlaps)); 51 | blendColor.set(LO_COLOR); 52 | blendColor.lerp(HI_COLOR, alpha); 53 | return blendColor; 54 | } 55 | @Override public String getMessage() {return "" + overlaps;} 56 | } 57 | -------------------------------------------------------------------------------- /demos-core/src/com/matthewmichelotti/collider/demos/comps/CMorphStickySpinner.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2013-2014 Matthew D. Michelotti 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.matthewmichelotti.collider.demos.comps; 18 | 19 | import com.badlogic.gdx.graphics.Color; 20 | import com.matthewmichelotti.collider.HBCircle; 21 | import com.matthewmichelotti.collider.demos.Component; 22 | import com.matthewmichelotti.collider.demos.FunctionEvent; 23 | import com.matthewmichelotti.collider.demos.Game; 24 | import com.matthewmichelotti.collider.demos.PosAndVel; 25 | 26 | /** 27 | * Generates morphing sticky circles in a radial pattern. 28 | * @author Matthew Michelotti 29 | */ 30 | public class CMorphStickySpinner extends Component { 31 | private double baseAngle = .1; 32 | 33 | public CMorphStickySpinner(final double x, final double y) 34 | { 35 | super(Game.engine.makeCircle()); 36 | HBCircle circ = circ(); 37 | circ.setDiam(CEnemyShip.DIAM); 38 | circ.setPos(x, y); 39 | circ.setGroup(-1); 40 | circ.commit(Double.POSITIVE_INFINITY); 41 | 42 | final double offset = .5*(CEnemyShip.DIAM + CMorphBullet.DIAM); 43 | 44 | Game.engine.addEvent(new FunctionEvent() { 45 | @Override public void resolve() { 46 | for(int i = 0; i < 3; i++) { 47 | double angle = baseAngle + i*2*Math.PI/3.0; 48 | new CMorphSticky(PosAndVel.radial(x, y, angle, offset, 440)); 49 | } 50 | baseAngle = (baseAngle + .15921) % (2*Math.PI); 51 | setTime(getTime() + .094); 52 | Game.engine.addEvent(this); 53 | } 54 | }); 55 | } 56 | 57 | @Override public void onCollide(Component other) {} 58 | @Override public void onSeparate(Component other) {} 59 | @Override public boolean canInteract(Component other) {return false;} 60 | @Override public boolean interactsWithBullets() {return false;} 61 | @Override public Color getColor() {return CEnemyShip.COLOR;} 62 | } 63 | -------------------------------------------------------------------------------- /core/src/com/matthewmichelotti/collider/ColliderOpts.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2013-2014 Matthew D. Michelotti 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.matthewmichelotti.collider; 18 | 19 | /** 20 | * Contains parameters for constructing a {@link Collider}. 21 | * @author Matthew Michelotti 22 | */ 23 | 24 | public final class ColliderOpts { 25 | /**Used to determine which pairs of HitBoxes should be tested for collisions.*/ 26 | public InteractTester interactTester; 27 | 28 | /** 29 | * An efficiency parameter representing the width and height of a 30 | * cell in the Collider grid. 31 | * The Collider references HitBoxes in a conceptually infinite grid 32 | * in order to reduce the number of collisions that need to be tested. 33 | * If your game uses a grid layout, it would be a good choice 34 | * to use the same cell width (or a power of two times that cell width). 35 | * Otherwise, a good guideline is that most of the HitBoxes should 36 | * have width and height less than cellWidth. 37 | */ 38 | public double cellWidth; 39 | 40 | /** 41 | * An efficiency parameter representing a bound on how far in advance 42 | * collisions/separations may be tested for. 43 | * This is used to avoid accumulating potential events in a priority 44 | * queue that take too long to be resolved, effectively creating a memory leak. 45 | * A good choice for most games is around 2 seconds. 46 | */ 47 | public double maxForesightTime; 48 | 49 | /** 50 | * Roughly the distance that two collided HitBoxes must be from each other before 51 | * a separated event is generated. This must be non-zero due to numerical stability 52 | * issues. A good choice for most games is around 1/10 of a "pixel", 53 | * since that won't be noticeable. 54 | */ 55 | public double separateBuffer; 56 | 57 | /**Creates a blank ColliderOpts object. Fields must be set manually.*/ 58 | public ColliderOpts() {} 59 | } 60 | -------------------------------------------------------------------------------- /demos-core/src/com/matthewmichelotti/collider/demos/comps/CCircFade.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2013-2014 Matthew D. Michelotti 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.matthewmichelotti.collider.demos.comps; 18 | 19 | import com.badlogic.gdx.graphics.Color; 20 | import com.matthewmichelotti.collider.HBCircle; 21 | import com.matthewmichelotti.collider.Normal; 22 | import com.matthewmichelotti.collider.demos.Component; 23 | import com.matthewmichelotti.collider.demos.FunctionEvent; 24 | import com.matthewmichelotti.collider.demos.Game; 25 | 26 | /** 27 | * A visual effect that displays a shrinking circle. 28 | * @author Matthew Michelotti 29 | */ 30 | public class CCircFade extends Component { 31 | private Color color; 32 | 33 | public CCircFade(HBCircle origCirc, Color color, double delay, Normal normal) { 34 | this(origCirc.getX(), origCirc.getY(), origCirc.getDiam(), color, delay, normal); 35 | } 36 | 37 | public CCircFade(double x, double y, double diam, Color color, double delay, Normal normal) { 38 | super(Game.engine.makeCircle()); 39 | HBCircle circ = circ(); 40 | circ.setGroup(-1); 41 | circ.setX(x); 42 | circ.setY(y); 43 | circ.setDiam(diam); 44 | 45 | this.color = color; 46 | 47 | double vel = .5*circ.getDiam()/delay; 48 | if(normal != null) { 49 | circ.setVel(vel*normal.getUnitX(), vel*normal.getUnitY()); 50 | } 51 | circ.setVelDiam(-2*vel); 52 | 53 | double endTime = circ.getTime() + .99*delay; 54 | circ.commit(endTime); 55 | 56 | Game.engine.addEvent(new FunctionEvent(endTime) { 57 | @Override public void resolve() {delete();} 58 | }); 59 | } 60 | 61 | @Override public boolean canInteract(Component o) {return false;} 62 | @Override public boolean interactsWithBullets() {return false;} 63 | @Override public void onCollide(Component other) {} 64 | @Override public void onSeparate(Component other) {} 65 | @Override public Color getColor() {return color;} 66 | } 67 | -------------------------------------------------------------------------------- /demos-core/src/com/matthewmichelotti/collider/demos/comps/CStickyGun.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2013-2014 Matthew D. Michelotti 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.matthewmichelotti.collider.demos.comps; 18 | 19 | import com.badlogic.gdx.graphics.Color; 20 | import com.matthewmichelotti.collider.HBCircle; 21 | import com.matthewmichelotti.collider.demos.Component; 22 | import com.matthewmichelotti.collider.demos.FunctionEvent; 23 | import com.matthewmichelotti.collider.demos.Game; 24 | import com.matthewmichelotti.collider.demos.PosAndVel; 25 | 26 | /** 27 | * Generates sticky circles shooting in a given direction 28 | * with some randomness. 29 | * @author Matthew Michelotti 30 | */ 31 | public class CStickyGun extends Component { 32 | private final static double BASE_VEL = 1500; 33 | 34 | public CStickyGun(final double x, final double y, final double baseAngle, 35 | final double shotDiam) 36 | { 37 | super(Game.engine.makeCircle()); 38 | HBCircle circ = circ(); 39 | circ.setDiam(CEnemyShip.DIAM); 40 | circ.setPos(x, y); 41 | circ.setGroup(-1); 42 | circ.commit(Double.POSITIVE_INFINITY); 43 | 44 | final double vel = BASE_VEL/shotDiam; 45 | final double period = 1.5*shotDiam/vel; 46 | final double offset = .5*(CEnemyShip.DIAM + shotDiam); 47 | 48 | Game.engine.addEvent(new FunctionEvent() { 49 | @Override public void resolve() { 50 | double angle = baseAngle + .45*(.5 - Math.random()); 51 | new CSticky(shotDiam, PosAndVel.radial(x, y, angle, offset, vel)); 52 | setTime(getTime() + period); 53 | Game.engine.addEvent(this); 54 | } 55 | }); 56 | } 57 | 58 | @Override public void onCollide(Component other) {} 59 | @Override public void onSeparate(Component other) {} 60 | @Override public boolean canInteract(Component other) {return false;} 61 | @Override public boolean interactsWithBullets() {return false;} 62 | @Override public Color getColor() {return CEnemyShip.COLOR;} 63 | } 64 | -------------------------------------------------------------------------------- /demos-core/src/com/matthewmichelotti/collider/demos/comps/CBullet.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2013-2014 Matthew D. Michelotti 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.matthewmichelotti.collider.demos.comps; 18 | 19 | import com.badlogic.gdx.graphics.Color; 20 | import com.matthewmichelotti.collider.HBCircle; 21 | import com.matthewmichelotti.collider.demos.Component; 22 | import com.matthewmichelotti.collider.demos.Game; 23 | import com.matthewmichelotti.collider.demos.GameEngine; 24 | import com.matthewmichelotti.collider.demos.PosAndVel; 25 | 26 | /** 27 | * A bullet primarily used in the danmaku scenarios. 28 | * @author Matthew Michelotti 29 | */ 30 | public class CBullet extends Component { 31 | public final static double DIAM = 10; 32 | public final static Color COLOR = new Color(1.0f, 1.0f, 0.1f, 1.0f); 33 | 34 | private boolean fromEnemy; 35 | 36 | public CBullet(boolean fromEnemy, PosAndVel pos) { 37 | super(Game.engine.makeCircle()); 38 | this.fromEnemy = fromEnemy; 39 | HBCircle circ = circ(); 40 | circ.setPos(pos.x, pos.y); 41 | circ.setVel(pos.vx, pos.vy); 42 | circ.setDiam(DIAM); 43 | circ.setGroup(GameEngine.GROUP_BULLET); 44 | circ.commit(Double.POSITIVE_INFINITY); 45 | if(!isInBounds()) throw new RuntimeException(); 46 | } 47 | 48 | @Override public boolean canInteract(Component o) { 49 | if(o instanceof CBounds) return true; 50 | if(o instanceof CTarget) return ((CTarget)o).isEnemy() != fromEnemy; 51 | return false; 52 | } 53 | @Override public boolean interactsWithBullets() {return false;} 54 | 55 | @Override 56 | public void onCollide(Component other) { 57 | if(other instanceof CBounds) return; 58 | new CCircFade(circ(), COLOR, .15, hitBox().getNormal(other.hitBox())); 59 | delete(); 60 | ((CTarget)other).hit(); 61 | } 62 | 63 | @Override public void onSeparate(Component other) { 64 | if(other instanceof CBounds) delete(); 65 | } 66 | 67 | @Override public Color getColor() {return COLOR;} 68 | } 69 | -------------------------------------------------------------------------------- /core/src/com/matthewmichelotti/collider/util/ColliderProcess.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2013-2014 Matthew D. Michelotti 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.matthewmichelotti.collider.util; 18 | 19 | import com.matthewmichelotti.collider.Collider; 20 | import com.matthewmichelotti.collider.ColliderEvent; 21 | import com.matthewmichelotti.collider.ColliderOpts; 22 | 23 | /** 24 | * A ContProcess implementation wrapped around a Collider. 25 | * 26 | * @author Matthew Michelotti 27 | */ 28 | public class ColliderProcess implements ContProcess { 29 | private Collider collider; 30 | private ColliderListener listener; 31 | 32 | /** 33 | * Constructs a new ColliderProcess. 34 | * @param collider The underlying Collider object. 35 | * @param listener A listener to handle events from the Collider. 36 | */ 37 | public ColliderProcess(Collider collider, ColliderListener listener) { 38 | if(collider == null || listener == null) throw new IllegalArgumentException(); 39 | this.collider = collider; 40 | this.listener = listener; 41 | } 42 | 43 | /** 44 | * Constructs a new ColliderProcess. 45 | * @param opts A new Collider object will be constructed from these options. 46 | * @param listener A listener to handle events from the Collider. 47 | */ 48 | public ColliderProcess(ColliderOpts opts, ColliderListener listener) { 49 | this(new Collider(opts), listener); 50 | } 51 | 52 | @Override 53 | public double peekNextEventTime() { 54 | return collider.peekNextEventTime(); 55 | } 56 | 57 | @Override 58 | public void stepToTime(double time) { 59 | ColliderEvent evt = collider.stepToTime(time, false); 60 | if(evt != null) throw new RuntimeException(); 61 | } 62 | 63 | @Override 64 | public void resolveEvent() { 65 | double time = collider.getTime(); 66 | ColliderEvent evt = collider.stepToTime(time); 67 | if(evt != null) { 68 | if(evt.isCollision()) listener.collision(evt); 69 | else if(evt.isSeparation()) listener.separation(evt); 70 | } 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /demos-core/src/com/matthewmichelotti/collider/demos/comps/CVarietyGun.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2013-2014 Matthew D. Michelotti 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.matthewmichelotti.collider.demos.comps; 18 | 19 | import com.badlogic.gdx.graphics.Color; 20 | import com.matthewmichelotti.collider.HBCircle; 21 | import com.matthewmichelotti.collider.demos.Component; 22 | import com.matthewmichelotti.collider.demos.FunctionEvent; 23 | import com.matthewmichelotti.collider.demos.Game; 24 | import com.matthewmichelotti.collider.demos.PosAndVel; 25 | 26 | /** 27 | * Shoots circular/rectangular bullets moving in a wave, 28 | * or shoots morphing bullets. 29 | * Direction is somewhat random. 30 | * @author Matthew Michelotti 31 | */ 32 | public class CVarietyGun extends Component { 33 | public CVarietyGun(final double x, final double y, final double baseAngle) 34 | { 35 | super(Game.engine.makeCircle()); 36 | HBCircle circ = circ(); 37 | circ.setDiam(CEnemyShip.DIAM); 38 | circ.setPos(x, y); 39 | circ.setGroup(-1); 40 | circ.commit(Double.POSITIVE_INFINITY); 41 | 42 | final double offset = .5*(CEnemyShip.DIAM + CMorphBullet.DIAM); 43 | 44 | Game.engine.addEvent(new FunctionEvent() { 45 | @Override public void resolve() { 46 | double angle = baseAngle + .5*Math.PI*(.5 - Math.random()); 47 | PosAndVel pos = PosAndVel.radial(x, y, angle, offset, 150); 48 | switch(Game.rand.nextInt(3)) { 49 | case 0: new CMorphBullet(pos); break; 50 | case 1: new CWaveBullet(pos, false); break; 51 | case 2: new CWaveBullet(pos, true); break; 52 | } 53 | setTime(getTime() + .9); 54 | Game.engine.addEvent(this); 55 | } 56 | }); 57 | } 58 | 59 | @Override public void onCollide(Component other) {} 60 | @Override public void onSeparate(Component other) {} 61 | @Override public boolean canInteract(Component other) {return false;} 62 | @Override public boolean interactsWithBullets() {return false;} 63 | @Override public Color getColor() {return CEnemyShip.COLOR;} 64 | } 65 | -------------------------------------------------------------------------------- /demos-core/src/com/matthewmichelotti/collider/demos/comps/CSticky.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2013-2014 Matthew D. Michelotti 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.matthewmichelotti.collider.demos.comps; 18 | 19 | import com.badlogic.gdx.graphics.Color; 20 | import com.matthewmichelotti.collider.HBCircle; 21 | import com.matthewmichelotti.collider.demos.Component; 22 | import com.matthewmichelotti.collider.demos.Game; 23 | import com.matthewmichelotti.collider.demos.PosAndVel; 24 | 25 | /** 26 | * A circle that will stop moving when it encounters another 27 | * CSticky or CTarget. 28 | * @author Matthew Michelotti 29 | */ 30 | public class CSticky extends Component { 31 | public final static Color COLOR = new Color(0.4f, 0.9f, 1.0f, 1.0f); 32 | private double stuckTime = -1; 33 | 34 | public CSticky(double diam, PosAndVel pos) { 35 | super(Game.engine.makeCircle()); 36 | HBCircle circ = circ(); 37 | circ.setPos(pos.x, pos.y); 38 | circ.setVel(pos.vx, pos.vy); 39 | circ.setDiam(diam); 40 | circ.commit(Double.POSITIVE_INFINITY); 41 | if(!isInBounds()) throw new RuntimeException(); 42 | } 43 | 44 | @Override public boolean canInteract(Component o) { 45 | return o instanceof CTarget || o instanceof CBounds || o instanceof CSticky; 46 | } 47 | @Override public boolean interactsWithBullets() {return false;} 48 | 49 | @Override 50 | public void onCollide(Component other) { 51 | if(other instanceof CBounds) return; 52 | double time = Game.engine.getTime(); 53 | if(stuckTime >= 0.0 && stuckTime < time) return; 54 | if(stuckTime < 0.0) stuckTime = time; 55 | if(stuckTime == time && hitBox().getOverlap(other.hitBox()) > .1) { 56 | delete(); 57 | } 58 | else { 59 | hitBox().setVel(0.0, 0.0); 60 | hitBox().commit(Double.POSITIVE_INFINITY); 61 | } 62 | if(other instanceof CTarget) ((CTarget)other).hit(); 63 | } 64 | 65 | @Override public void onSeparate(Component other) { 66 | if(other instanceof CBounds) delete(); 67 | } 68 | 69 | @Override public Color getColor() {return COLOR;} 70 | } 71 | -------------------------------------------------------------------------------- /demos-core/src/com/matthewmichelotti/collider/demos/comps/CMorphBullet.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2013-2014 Matthew D. Michelotti 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.matthewmichelotti.collider.demos.comps; 18 | 19 | import com.badlogic.gdx.graphics.Color; 20 | import com.matthewmichelotti.collider.HBCircle; 21 | import com.matthewmichelotti.collider.demos.Component; 22 | import com.matthewmichelotti.collider.demos.Game; 23 | import com.matthewmichelotti.collider.demos.GameEngine; 24 | import com.matthewmichelotti.collider.demos.PosAndVel; 25 | import com.matthewmichelotti.collider.demos.WaveUpdater; 26 | 27 | /** 28 | * A bullet that resizes its diameter in a wave-like fashion. 29 | * @author Matthew Michelotti 30 | */ 31 | public class CMorphBullet extends Component { 32 | public final static double DIAM = 40; 33 | 34 | public CMorphBullet(PosAndVel pos) { 35 | super(Game.engine.makeCircle()); 36 | final HBCircle circ = circ(); 37 | circ.setPos(pos.x, pos.y); 38 | circ.setVel(pos.vx, pos.vy); 39 | circ.setGroup(GameEngine.GROUP_BULLET); 40 | new WaveUpdater(DIAM, -35, 2) { 41 | @Override protected boolean isValid() {return !isDeleted();} 42 | @Override protected void update(double value, double vel, double endTime) { 43 | circ.setDiam(value); 44 | circ.setVelDiam(vel); 45 | circ.commit(endTime); 46 | } 47 | }; 48 | if(!isInBounds()) throw new RuntimeException(); 49 | } 50 | 51 | @Override public boolean canInteract(Component o) { 52 | if(o instanceof CBounds) return true; 53 | if(o instanceof CTarget) return !((CTarget)o).isEnemy(); 54 | return false; 55 | } 56 | @Override public boolean interactsWithBullets() {return false;} 57 | 58 | @Override 59 | public void onCollide(Component other) { 60 | if(!(other instanceof CTarget)) return; 61 | new CCircFade(circ(), CBullet.COLOR, .15, hitBox().getNormal(other.hitBox())); 62 | delete(); 63 | ((CTarget)other).hit(); 64 | } 65 | 66 | @Override public void onSeparate(Component other) { 67 | if(other instanceof CBounds) delete(); 68 | } 69 | 70 | @Override public Color getColor() {return CBullet.COLOR;} 71 | } 72 | -------------------------------------------------------------------------------- /core/src/com/matthewmichelotti/collider/InteractTester.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2013-2014 Matthew D. Michelotti 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.matthewmichelotti.collider; 18 | 19 | /** 20 | * User-implemented interface to determine which HitBoxes are allowed to interact 21 | * with each other. This reduces the number of collisions that need to be tested. 22 | * 23 | * @author Matthew Michelotti 24 | */ 25 | public interface InteractTester { 26 | /** 27 | * Determines whether collision/separation events should be generated for a given 28 | * pair of HitBoxes. The order of the two HitBoxes should not matter. 29 | * If the same HitBoxes are input multiple times, 30 | * the return value must be the same. 31 | * As an exception to this rule, the return value may change if this method 32 | * is called after 33 | * 40 | * @param a First HitBox. 41 | * @param b Second HitBox. 42 | * @return True if collision/separation events should be generated 43 | * for this pair of HitBoxes. 44 | */ 45 | public boolean canInteract(HitBox a, HitBox b); 46 | 47 | /** 48 | * Determines which groups a given HitBox is allowed to interact with. 49 | * Do not need to list groups in order, but there should be no duplicates. 50 | * If this method does not list a certain group as interactable with 51 | * a certain HitBox, then {@link #canInteract(HitBox, 52 | * HitBox)} must return false 53 | * when comparing that HitBox with any HitBox of the given group. 54 | * @param hitBox HitBox to test interactivity of. 55 | * @return an array of groups that can be interacted with. For efficiency, this 56 | * array should not be constructed each time the method is called. 57 | * This array will not be modified. 58 | * Returning null will be treated the same as returning an empty array 59 | * @see HitBox#setGroup(int) 60 | */ 61 | public int[] getInteractGroups(HitBox hitBox); 62 | } 63 | -------------------------------------------------------------------------------- /demos-desktop/demos-desktop.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | -------------------------------------------------------------------------------- /demos-core/src/com/matthewmichelotti/collider/demos/WaveUpdater.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2013-2014 Matthew D. Michelotti 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.matthewmichelotti.collider.demos; 18 | 19 | /** 20 | * Creates a {@link FunctionEvent} to periodically 21 | * adjust values and velocities in a wave-like fashion. 22 | * @author Matthew Michelotti 23 | */ 24 | public abstract class WaveUpdater { 25 | private final static double[] SIN_SAMPLES; 26 | 27 | static { 28 | double[] halfSinSamples = new double[] {.25, .365, .45, .5, .55, .635, .75}; 29 | SIN_SAMPLES = new double[halfSinSamples.length*2]; 30 | for(int i = 0; i < halfSinSamples.length; i++) { 31 | SIN_SAMPLES[i] = .5*halfSinSamples[i]; 32 | SIN_SAMPLES[halfSinSamples.length + i] = .5 + .5*halfSinSamples[i]; 33 | } 34 | } 35 | 36 | private double mean; 37 | private double amplitude; 38 | private double period; 39 | private int index = 0; 40 | 41 | protected WaveUpdater(double mean, double amplitude, double period) { 42 | this.mean = mean; 43 | this.amplitude = amplitude; 44 | this.period = period; 45 | 46 | if(!isValid()) return; 47 | double endTime = step(0.0, SIN_SAMPLES[0]); 48 | Game.engine.addEvent(new FunctionEvent(endTime) { 49 | @Override public void resolve() { 50 | if(!isValid()) return; 51 | double startFrac = SIN_SAMPLES[index]; 52 | index++; 53 | double endFrac; 54 | if(index < SIN_SAMPLES.length) { 55 | endFrac = SIN_SAMPLES[index]; 56 | } 57 | else { 58 | index = 0; 59 | endFrac = 1.0 + SIN_SAMPLES[0]; 60 | } 61 | double endTime = step(startFrac, endFrac); 62 | setTime(endTime); 63 | Game.engine.addEvent(this); 64 | } 65 | }); 66 | } 67 | 68 | protected abstract boolean isValid(); 69 | protected abstract void update(double value, double vel, double endTime); 70 | 71 | private double step(double startFrac, double endFrac) { 72 | double duration = (endFrac - startFrac)*period; 73 | double endTime = Game.engine.getTime() + duration; 74 | double startValue = mean + amplitude*Math.sin(2*Math.PI*startFrac); 75 | double endValue = mean + amplitude*Math.sin(2*Math.PI*endFrac); 76 | update(startValue, (endValue - startValue)/duration, endTime); 77 | return endTime; 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /demos-core/src/com/matthewmichelotti/collider/demos/Component.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2013-2014 Matthew D. Michelotti 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.matthewmichelotti.collider.demos; 18 | 19 | import com.badlogic.gdx.graphics.Color; 20 | import com.matthewmichelotti.collider.HBCircle; 21 | import com.matthewmichelotti.collider.HBPositioned; 22 | import com.matthewmichelotti.collider.HBRect; 23 | 24 | /** 25 | * A single entity in the game. 26 | * Each Component is associated with a single 27 | * {@link com.matthewmichelotti.collider.HitBox HitBox}. 28 | * Displays that HitBox in a given color, and 29 | * may also display a message. 30 | * Subclasses implement functionality for handling 31 | * collisions and separations with other Components. 32 | * @author Matthew Michelotti 33 | */ 34 | public abstract class Component implements Comparable { 35 | private static int NEXT_ID = 0; 36 | private final int id; 37 | private HBPositioned hitBox; 38 | 39 | protected Component(HBPositioned hitBox) { 40 | if(hitBox == null) throw new IllegalArgumentException(); 41 | this.hitBox = hitBox; 42 | hitBox.setOwner(this); 43 | id = NEXT_ID++; 44 | Game.engine.addComp(this); 45 | } 46 | 47 | protected final void delete() { 48 | if(hitBox == null) return; 49 | Game.engine.removeComp(this); 50 | hitBox.free(); 51 | hitBox = null; 52 | } 53 | 54 | protected final boolean isInBounds() {return Game.engine.isInBounds(hitBox);} 55 | public final boolean isDeleted() {return hitBox == null;} 56 | public final int getId() {return id;} 57 | 58 | public final HBPositioned hitBox() {return hitBox;} 59 | public final HBCircle circ() {return (HBCircle)hitBox;} 60 | public final HBRect rect() {return (HBRect)hitBox;} 61 | public final boolean isRect() {return hitBox instanceof HBRect;} 62 | public final boolean isCirc() {return hitBox instanceof HBCircle;} 63 | 64 | public abstract void onCollide(Component other); 65 | public abstract void onSeparate(Component other); 66 | public abstract boolean canInteract(Component other); 67 | public abstract boolean interactsWithBullets(); 68 | public abstract Color getColor(); 69 | public String getMessage() {return null;} 70 | 71 | @Override public final int compareTo(Component o) { 72 | return id - o.id; 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /core/src/com/matthewmichelotti/collider/ColliderEvent.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2013-2014 Matthew D. Michelotti 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.matthewmichelotti.collider; 18 | 19 | /** 20 | * A collision or seperation event between two HitBoxes. 21 | * 22 | * @author Matthew Michelotti 23 | */ 24 | public final class ColliderEvent { 25 | private HitBox a, b; 26 | private boolean collided; 27 | 28 | ColliderEvent() {} 29 | 30 | void init(HitBox a, HitBox b, boolean collided) { 31 | if(a == null || b == null) throw new RuntimeException(); 32 | this.a = a; 33 | this.b = b; 34 | this.collided = collided; 35 | } 36 | 37 | void clear() { 38 | this.a = null; 39 | this.b = null; 40 | } 41 | 42 | boolean isInitialized() {return a != null;} 43 | boolean involves(HitBox hitBox) {return a == hitBox || b == hitBox;} 44 | 45 | /** 46 | * Returns the time of the event. 47 | * This is the same as calling {@link Collider#getTime()}. 48 | * @return The time of the event. 49 | */ 50 | public double getTime() {return a.getTime();} 51 | 52 | /** 53 | * Returns true if this is a collision event. 54 | * @return True if this is a collision event. 55 | */ 56 | public boolean isCollision() {return collided;} 57 | 58 | /** 59 | * Returns true if this is a separation event. 60 | * @return True if this is a separation event. 61 | */ 62 | public boolean isSeparation() {return !collided;} 63 | 64 | /** 65 | * Returns the first HitBox involved in this event. 66 | * @return The first HitBox involved in this event. 67 | */ 68 | public HitBox getFirst() {return a;} 69 | 70 | /** 71 | * Returns the second HitBox involved in this event. 72 | * @return The second HitBox involved in this event. 73 | */ 74 | public HitBox getSecond() {return b;} 75 | 76 | /** 77 | * Given one of the HitBoxes involved in this event, this is a convenience method 78 | * for obtaining the other involved HitBox. 79 | * Like using (hitBox == getFirst()) ? getSecond() : getFirst(). 80 | * @param hitBox One of the HitBoxes involved in this event. 81 | * @return The other HitBox involved in this event. 82 | */ 83 | public HitBox getOther(HitBox hitBox) { 84 | if(a == hitBox) return b; 85 | else if(b == hitBox) return a; 86 | else throw new IllegalArgumentException(); 87 | } 88 | } 89 | -------------------------------------------------------------------------------- /core/src_gdx_util/com/matthewmichelotti/collider/ArrayReflection.java: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright 2011 See AUTHORS file. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | ******************************************************************************/ 16 | //!!!NOTICE!!! this file has been modified by Matthew Michelotti. 17 | //This file was originally part of the libGDX library 18 | //(https://github.com/libgdx/libgdx) under the Apache License as described 19 | //above. The AUTHORS file specified above can be found at 20 | //https://github.com/libgdx/libgdx/blob/master/AUTHORS . 21 | //The contents of the authors file is as follows: 22 | // 23 | // # This is the official list of the AUTHORS of libgdx 24 | // # for copyright purposes. 25 | // # This file is distinct from the CONTRIBUTORS files. 26 | // # See the latter for an explanation. 27 | // 28 | // # Names should be added to this file as 29 | // # Name or Organization 30 | // # The email address is not required for organizations. 31 | // Mario Zechner 32 | // Nathan Sweet 33 | // 34 | //End of AUTHORS file. 35 | // 36 | //All changes to this file are marked with comments starting with 37 | //my initials (MM). 38 | 39 | 40 | //MM: changed package name 41 | //package com.badlogic.gdx.utils.reflect; 42 | package com.matthewmichelotti.collider; 43 | 44 | /** Utilities for Array reflection. 45 | * @author nexsoftware */ 46 | //MM: changed class from public to package-private 47 | final class ArrayReflection { 48 | 49 | /** Creates a new array with the specified component type and length. */ 50 | static public Object newInstance (Class c, int size) { 51 | return java.lang.reflect.Array.newInstance(c, size); 52 | } 53 | 54 | /** Returns the length of the supplied array. */ 55 | static public int getLength (Object array) { 56 | return java.lang.reflect.Array.getLength(array); 57 | } 58 | 59 | /** Returns the value of the indexed component in the supplied array. */ 60 | static public Object get (Object array, int index) { 61 | return java.lang.reflect.Array.get(array, index); 62 | } 63 | 64 | /** Sets the value of the indexed component in the supplied array to the supplied value. */ 65 | static public void set (Object array, int index, Object value) { 66 | java.lang.reflect.Array.set(array, index, value); 67 | } 68 | 69 | } 70 | -------------------------------------------------------------------------------- /demos-core/src/com/matthewmichelotti/collider/demos/comps/CMorphSticky.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2013-2014 Matthew D. Michelotti 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.matthewmichelotti.collider.demos.comps; 18 | 19 | import com.badlogic.gdx.graphics.Color; 20 | import com.matthewmichelotti.collider.HBCircle; 21 | import com.matthewmichelotti.collider.demos.Component; 22 | import com.matthewmichelotti.collider.demos.Game; 23 | import com.matthewmichelotti.collider.demos.PosAndVel; 24 | import com.matthewmichelotti.collider.demos.WaveUpdater; 25 | 26 | /** 27 | * A sticky circle that resizes its diameter in a wave-like fashion. 28 | * Circle will stop moving when it encounters another CMorphSticky or CTarget. 29 | * @author Matthew Michelotti 30 | */ 31 | public class CMorphSticky extends Component { 32 | private double stuckTime = -1; 33 | 34 | public CMorphSticky(PosAndVel pos) { 35 | super(Game.engine.makeCircle()); 36 | final HBCircle circ = circ(); 37 | circ.setPos(pos.x, pos.y); 38 | circ.setVel(pos.vx, pos.vy); 39 | circ.commit(Double.POSITIVE_INFINITY); 40 | new WaveUpdater(CMorphBullet.DIAM, -34, 1) { 41 | @Override protected boolean isValid() {return !isDeleted() && stuckTime < 0.0;} 42 | @Override protected void update(double value, double vel, double endTime) { 43 | circ.setDiam(value); 44 | circ.setVelDiam(vel); 45 | circ.commit(endTime); 46 | } 47 | }; 48 | if(!isInBounds()) throw new RuntimeException(); 49 | } 50 | 51 | @Override public boolean canInteract(Component o) { 52 | return o instanceof CTarget || o instanceof CBounds || o instanceof CMorphSticky; 53 | } 54 | @Override public boolean interactsWithBullets() {return false;} 55 | 56 | @Override 57 | public void onCollide(Component other) { 58 | if(other instanceof CBounds) return; 59 | double time = Game.engine.getTime(); 60 | if(stuckTime >= 0.0 && stuckTime < time) return; 61 | if(stuckTime < 0.0) stuckTime = time; 62 | if(stuckTime == time && hitBox().getOverlap(other.hitBox()) > .1) { 63 | delete(); 64 | } 65 | else { 66 | hitBox().setVel(0.0, 0.0); 67 | circ().setVelDiam(0.0); 68 | hitBox().commit(Double.POSITIVE_INFINITY); 69 | } 70 | if(other instanceof CTarget) ((CTarget)other).hit(); 71 | } 72 | 73 | @Override public void onSeparate(Component other) { 74 | if(other instanceof CBounds) delete(); 75 | } 76 | 77 | @Override public Color getColor() {return CSticky.COLOR;} 78 | } 79 | -------------------------------------------------------------------------------- /demos-core/src/com/matthewmichelotti/collider/demos/Demos.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2013-2014 Matthew D. Michelotti 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.matthewmichelotti.collider.demos; 18 | 19 | import com.badlogic.gdx.ApplicationListener; 20 | import com.badlogic.gdx.Gdx; 21 | import com.badlogic.gdx.InputProcessor; 22 | import com.badlogic.gdx.graphics.GL20; 23 | import com.badlogic.gdx.utils.TimeUtils; 24 | 25 | /** 26 | * Runs all of the demos for the Collider library. 27 | * @author Matthew Michelotti 28 | */ 29 | public class Demos implements ApplicationListener { 30 | private long startTime; 31 | private int scenarioI = 0; 32 | private boolean clicked = false; 33 | 34 | @Override 35 | public void create() { 36 | InputProcessor inputProcessor = new InputProcessor() { 37 | @Override public boolean keyDown(int keycode) {return false;} 38 | @Override public boolean keyUp(int keycode) {return false;} 39 | @Override public boolean keyTyped(char character) {return false;} 40 | @Override public boolean touchDown(int screenX, int screenY, int pointer, int button) { 41 | return true; 42 | } 43 | @Override public boolean touchUp(int screenX, int screenY, int pointer, 44 | int button) { 45 | clicked = true; 46 | return true; 47 | } 48 | @Override public boolean touchDragged(int screenX, int screenY, int pointer) { 49 | return false; 50 | } 51 | @Override public boolean mouseMoved(int screenX, int screenY) {return false;} 52 | @Override public boolean scrolled(int amount) {return false;} 53 | }; 54 | Gdx.input.setInputProcessor(inputProcessor); 55 | 56 | Scenarios.initScenario(0); 57 | startTime = TimeUtils.nanoTime(); 58 | } 59 | 60 | @Override 61 | public void dispose() { 62 | Game.engine.dispose(); 63 | } 64 | 65 | @Override 66 | public void render() { 67 | long time = TimeUtils.nanoTime(); 68 | if(clicked) { 69 | clicked = false; 70 | scenarioI = (scenarioI + 1) % Scenarios.NUM_SCENARIOS; 71 | Scenarios.initScenario(scenarioI); 72 | startTime = time; 73 | } 74 | Game.engine.stepToTime((TimeUtils.nanoTime() - startTime)*1e-9); 75 | 76 | Gdx.gl.glClearColor(0, 0, 0, 1); 77 | Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT); 78 | 79 | Game.engine.render(true); 80 | } 81 | 82 | @Override 83 | public void resize(int width, int height) { 84 | } 85 | 86 | @Override 87 | public void pause() { 88 | } 89 | 90 | @Override 91 | public void resume() { 92 | } 93 | } 94 | -------------------------------------------------------------------------------- /core/src/com/matthewmichelotti/collider/IntBox.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2013-2014 Matthew D. Michelotti 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.matthewmichelotti.collider; 18 | 19 | 20 | final class IntBox { 21 | int l, b, r, t; 22 | 23 | IntBox() {} 24 | 25 | // void restrict(IntBox bound) { 26 | // if(bound == null) return; 27 | // if(bound.l > l) l = bound.l; 28 | // if(bound.r < r) r = bound.r; 29 | // if(bound.b > b) b = bound.b; 30 | // if(bound.t < t) t = bound.t; 31 | // } 32 | 33 | static class Iterator implements Int2DIterator { 34 | private int x, y; 35 | private int l, r, t = -1; 36 | 37 | Iterator() {} 38 | 39 | Iterator(IntBox box) {init(box);} 40 | 41 | void init(IntBox box) { 42 | int b; 43 | if(box == null) {l = 0; r = 0; b = 0; t = -1;} 44 | else {l = box.l; r = box.r; b = box.b; t = box.t;} 45 | if(r < l) t = b - 1; 46 | x = l; 47 | y = b; 48 | } 49 | 50 | @Override public boolean isDone() {return y > t;} 51 | 52 | @Override 53 | public void next() { 54 | x++; 55 | if(x > r) { 56 | x = l; 57 | y++; 58 | } 59 | } 60 | 61 | @Override public int getX() {return x;} 62 | @Override public int getY() {return y;} 63 | } 64 | 65 | static class DiffIterator implements Int2DIterator { 66 | private int x, y; 67 | private int l, r, t = -1; 68 | private int sl, sb, sr, st; 69 | 70 | DiffIterator() {} 71 | 72 | DiffIterator(IntBox box, IntBox subBox) {init(box, subBox);} 73 | 74 | void init(IntBox box, IntBox subBox) { 75 | int b; 76 | if(box == null) {l = 0; r = 0; b = 0; t = -1;} 77 | else {l = box.l; r = box.r; b = box.b; t = box.t;} 78 | if(r < l) t = b - 1; 79 | if(subBox == null) {sl = 0; sr = 0; sb = 0; st = -1;} 80 | else {sl = subBox.l; sr = subBox.r; sb = subBox.b; st = subBox.t;} 81 | x = l - 1; 82 | y = b; 83 | next(); 84 | } 85 | 86 | @Override public boolean isDone() {return y > t;} 87 | 88 | @Override 89 | public void next() { 90 | x++; 91 | if(x > r) { 92 | x = l; 93 | y++; 94 | } 95 | if(y < sb || y > st || x < sl || x > sr) return; 96 | if(sr >= r) { 97 | if(sl <= l) y = st + 1; 98 | else { 99 | x = l; 100 | y++; 101 | } 102 | } 103 | else { 104 | x = sr + 1; 105 | } 106 | } 107 | 108 | @Override public int getX() {return x;} 109 | @Override public int getY() {return y;} 110 | } 111 | } 112 | -------------------------------------------------------------------------------- /core/src/com/matthewmichelotti/collider/HBCircle.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2013-2014 Matthew D. Michelotti 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.matthewmichelotti.collider; 18 | 19 | /** 20 | * A circular HitBox. 21 | * @author Matthew Michelotti 22 | * @see Collider#makeCircle() 23 | */ 24 | public final class HBCircle extends HBPositioned { 25 | double startRad; 26 | double velRad; 27 | 28 | HBCircle(Collider collider) { 29 | super(collider); 30 | } 31 | 32 | @Override 33 | void init() { 34 | super.init(); 35 | this.startRad = 0.0; 36 | this.velRad = 0.0; 37 | } 38 | 39 | @Override 40 | void markTransitionStart() { 41 | double time = collider.getTime(); 42 | startRad = getRad(time); 43 | super.markTransitionStart(); 44 | } 45 | 46 | @Override 47 | public void free() { 48 | collider.free(this); 49 | super.free(); 50 | } 51 | 52 | /** 53 | * Set the diameter. 54 | * @param diam Diameter. 55 | */ 56 | public void setDiam(double diam) { 57 | collider.altering(this); 58 | this.startRad = .5*diam; 59 | } 60 | 61 | /** 62 | * Set the velocity of the diameter. 63 | * @param velDiam Velocity of the diameter. 64 | */ 65 | public void setVelDiam(double velDiam) { 66 | collider.altering(this); 67 | this.velRad = .5*velDiam; 68 | } 69 | 70 | double getRad(double time) {return startRad + (time - startTime)*velRad;} 71 | 72 | /** 73 | * Get the diameter. 74 | * @return Diameter. 75 | */ 76 | public double getDiam() {return 2*getRad(collider.getTime());} 77 | 78 | /** 79 | * Get the velocity of the diameter. 80 | * @return Velocity of the diameter. 81 | */ 82 | public double getVelDiam() {return 2*velRad;} 83 | 84 | double getStartEdgeComp(int edge) { 85 | return getStartPosComp(edge) + startRad; 86 | } 87 | 88 | double getVelEdgeComp(int edge) { 89 | return getVelComp(edge) + velRad; 90 | } 91 | 92 | @Override 93 | double getBoundEdgeComp(int edge, double startTime, double endTime) { 94 | double base = getStartEdgeComp(edge); 95 | double vel = getVelEdgeComp(edge); 96 | double evalTime = (vel > 0.0) ? endTime : startTime; 97 | return base + vel*(evalTime - this.startTime); 98 | } 99 | 100 | @Override 101 | boolean isMoving() { 102 | return velX != 0.0 || velY != 0.0 || velRad != 0.0; 103 | } 104 | 105 | @Override 106 | double getMaxBoundEdgeVel() { 107 | double vel = 0.0; 108 | double absVelRad = Arith.abs(velRad); 109 | for(int dir = 0; dir < 2; dir++) { 110 | vel = Math.max(vel, Arith.abs(getVelComp(dir)) + absVelRad); 111 | } 112 | return vel; 113 | } 114 | } 115 | -------------------------------------------------------------------------------- /demos-core/src/com/matthewmichelotti/collider/demos/comps/CPlayerShip.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2013-2014 Matthew D. Michelotti 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.matthewmichelotti.collider.demos.comps; 18 | 19 | import com.badlogic.gdx.graphics.Color; 20 | import com.matthewmichelotti.collider.HBCircle; 21 | import com.matthewmichelotti.collider.HBPositioned; 22 | import com.matthewmichelotti.collider.demos.FunctionEvent; 23 | import com.matthewmichelotti.collider.demos.Game; 24 | import com.matthewmichelotti.collider.demos.GameEngine; 25 | import com.matthewmichelotti.collider.demos.MousePosListener; 26 | import com.matthewmichelotti.collider.demos.PosAndVel; 27 | 28 | /** 29 | * Player-controlled ship used in danmaku scenarios. 30 | * @author Matthew Michelotti 31 | */ 32 | public class CPlayerShip extends CTarget implements MousePosListener { 33 | public final static Color COLOR = new Color(0.1f, 0.15f, 1.0f, 1.0f); 34 | 35 | private final static double VEL = 600; 36 | 37 | private double stopEndTime; 38 | private FunctionEvent stopEvent = new FunctionEvent() { 39 | @Override public void resolve() { 40 | hitBox().setVel(0.0, 0.0); 41 | hitBox().commit(stopEndTime); 42 | } 43 | }; 44 | 45 | public CPlayerShip() { 46 | super(Game.engine.makeCircle(), COLOR); 47 | HBCircle circ = circ(); 48 | circ.setDiam(10); 49 | circ.setPos(.5*GameEngine.SCREEN_WIDTH, .2*GameEngine.SCREEN_HEIGHT); 50 | circ.commit(Double.POSITIVE_INFINITY); 51 | 52 | Game.engine.addEvent(new FunctionEvent() { 53 | @Override public void resolve() { 54 | double x = hitBox().getX(); 55 | double y = hitBox().getY(); 56 | new CBullet(false, new PosAndVel(x + 10, y + 10, 0, 1000)); 57 | new CBullet(false, new PosAndVel(x - 10, y + 10, 0, 1000)); 58 | setTime(getTime() + .13); 59 | Game.engine.addEvent(this); 60 | } 61 | }); 62 | } 63 | 64 | @Override 65 | public void updateMousePos(double endTime, double mx, double my) { 66 | HBPositioned hitBox = hitBox(); 67 | if(endTime <= hitBox.getTime()) return; 68 | double x = hitBox.getX(); 69 | double y = hitBox.getY(); 70 | double dx = mx - x; 71 | double dy = my - y; 72 | double dmag = Math.sqrt(dx*dx + dy*dy); 73 | double arriveTime = hitBox.getTime() + dmag/VEL; 74 | if(arriveTime <= hitBox.getTime()) { 75 | hitBox.setVel(0.0, 0.0); 76 | hitBox.commit(endTime); 77 | return; 78 | } 79 | double ux = dx/dmag; 80 | double uy = dy/dmag; 81 | hitBox.setVel(VEL*ux, VEL*uy); 82 | if(endTime <= arriveTime) { 83 | hitBox.commit(endTime); 84 | return; 85 | } 86 | hitBox.commit(arriveTime); 87 | this.stopEndTime = endTime; 88 | stopEvent.setTime(arriveTime); 89 | Game.engine.addEvent(stopEvent); 90 | } 91 | } 92 | -------------------------------------------------------------------------------- /demos-core/src/com/matthewmichelotti/collider/demos/comps/CWaveBullet.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2013-2014 Matthew D. Michelotti 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.matthewmichelotti.collider.demos.comps; 18 | 19 | import com.badlogic.gdx.graphics.Color; 20 | import com.matthewmichelotti.collider.HBPositioned; 21 | import com.matthewmichelotti.collider.demos.Component; 22 | import com.matthewmichelotti.collider.demos.Game; 23 | import com.matthewmichelotti.collider.demos.GameEngine; 24 | import com.matthewmichelotti.collider.demos.PosAndVel; 25 | import com.matthewmichelotti.collider.demos.WaveUpdater; 26 | 27 | /** 28 | * A circular/rectangular bullet that moves in a wave-like pattern. 29 | * @author Matthew Michelotti 30 | */ 31 | public class CWaveBullet extends Component { 32 | public CWaveBullet(PosAndVel pos, boolean isRect) { 33 | super(isRect ? Game.engine.makeRect() : Game.engine.makeCircle()); 34 | if(isRect) rect().setDims(40.0, 20.0); 35 | else circ().setDiam(CMorphBullet.DIAM); 36 | 37 | final HBPositioned hitBox = hitBox(); 38 | hitBox.setGroup(GameEngine.GROUP_BULLET); 39 | 40 | final double startX = pos.x; 41 | final double startY = pos.y; 42 | final double baseVX = pos.vx; 43 | final double baseVY = pos.vy; 44 | final double startTime = Game.engine.getTime(); 45 | double velMag = Math.sqrt(baseVX*baseVX + baseVY*baseVY); 46 | if(velMag <= 0.0) throw new RuntimeException(); 47 | final double perpUX = -baseVY/velMag; 48 | final double perpUY = baseVX/velMag; 49 | 50 | new WaveUpdater(0, 40, 2) { 51 | @Override protected boolean isValid() {return !isDeleted();} 52 | @Override protected void update(double value, double vel, double endTime) { 53 | double timeDiff = Game.engine.getTime() - startTime; 54 | hitBox.setX(startX + baseVX*timeDiff + value*perpUX); 55 | hitBox.setY(startY + baseVY*timeDiff + value*perpUY); 56 | hitBox.setVel(baseVX + vel*perpUX, baseVY + vel*perpUY); 57 | hitBox.commit(endTime); 58 | } 59 | }; 60 | if(!isInBounds()) throw new RuntimeException(); 61 | } 62 | 63 | @Override public boolean canInteract(Component o) { 64 | if(o instanceof CBounds) return true; 65 | if(o instanceof CTarget) return !((CTarget)o).isEnemy(); 66 | return false; 67 | } 68 | @Override public boolean interactsWithBullets() {return false;} 69 | 70 | @Override 71 | public void onCollide(Component other) { 72 | if(!(other instanceof CTarget)) return; 73 | new CCircFade(circ(), CBullet.COLOR, .15, hitBox().getNormal(other.hitBox())); 74 | delete(); 75 | ((CTarget)other).hit(); 76 | } 77 | 78 | @Override public void onSeparate(Component other) { 79 | if(other instanceof CBounds) delete(); 80 | } 81 | 82 | @Override public Color getColor() {return CBullet.COLOR;} 83 | } 84 | -------------------------------------------------------------------------------- /core/src/com/matthewmichelotti/collider/util/ContProcesses.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2013-2014 Matthew D. Michelotti 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.matthewmichelotti.collider.util; 18 | 19 | import java.util.ArrayList; 20 | 21 | /** 22 | * Couples multiple {@link ContProcess} objects together 23 | * to handle their respective events in chronological order. 24 | * 25 | * @author Matthew Michelotti 26 | */ 27 | public class ContProcesses { 28 | private ArrayList processes = new ArrayList(); 29 | private double time = 0.0; 30 | 31 | /** 32 | * Constructs a new ContProcesses object. Time is initialized to zero. 33 | */ 34 | public ContProcesses() {} 35 | 36 | /** 37 | * Adds a new continuous-time process. The {@link ContProcess#stepToTime(double)} method 38 | * is called to advance the process to the current time of this object. No events should be scheduled 39 | * before this time. 40 | * @param process The new continuous-time process to add. 41 | * @return True iff the process was not already in this collection. 42 | */ 43 | public boolean addProcess(ContProcess process) { 44 | if(processes.indexOf(process) >= 0) return false; 45 | double nextEventTime = process.peekNextEventTime(); 46 | if(nextEventTime < time) throw new RuntimeException("process event time has already passed"); 47 | process.stepToTime(time); 48 | processes.add(process); 49 | return true; 50 | } 51 | 52 | /** 53 | * Removes a process. Methods of the given process will no longer be invoked. 54 | * @param process The process to remove. 55 | * @return True iff the process was found. 56 | */ 57 | public boolean removeProcess(ContProcess process) { 58 | return processes.remove(process); 59 | } 60 | 61 | /** 62 | * Returns the current time of the processes. 63 | * @return The current time. 64 | */ 65 | public double getTime() { 66 | return time; 67 | } 68 | 69 | /** 70 | * Advances all processes to the given time, resolving all events along the way. 71 | * All processes will share the same time whenever an event is resolved. 72 | * If events from two processes occur at the same time, the event for 73 | * the process that was added first will be resolved first. 74 | * @param newTime Time to advance the processes to. 75 | */ 76 | public void stepToTime(double newTime) { 77 | if(newTime < time) throw new RuntimeException(); 78 | while(true) { 79 | double minEvtTime = newTime; 80 | int minEvtTimeI = -1; 81 | for(int i = 0; i < processes.size(); i++) { 82 | double evtTime = processes.get(i).peekNextEventTime(); 83 | if(evtTime < time) throw new RuntimeException(); 84 | if(evtTime < minEvtTime) { 85 | minEvtTime = evtTime; 86 | minEvtTimeI = i; 87 | } 88 | } 89 | if(minEvtTime != time) { 90 | for(int i = 0; i < processes.size(); i++) { 91 | processes.get(i).stepToTime(minEvtTime); 92 | } 93 | } 94 | this.time = minEvtTime; 95 | if(minEvtTimeI < 0) break; 96 | processes.get(minEvtTimeI).resolveEvent(); 97 | } 98 | } 99 | } 100 | -------------------------------------------------------------------------------- /demos-core/src/com/matthewmichelotti/collider/demos/comps/CCoagParticle.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2013-2014 Matthew D. Michelotti 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.matthewmichelotti.collider.demos.comps; 18 | 19 | import com.badlogic.gdx.graphics.Color; 20 | import com.matthewmichelotti.collider.HBCircle; 21 | import com.matthewmichelotti.collider.HBRect; 22 | import com.matthewmichelotti.collider.demos.Component; 23 | import com.matthewmichelotti.collider.demos.Game; 24 | import com.matthewmichelotti.collider.demos.GameEngine; 25 | import com.matthewmichelotti.collider.demos.Geom; 26 | 27 | /** 28 | * A circular particle that can merge (aka coagulate) with other particles. 29 | * @author Matthew Michelotti 30 | */ 31 | public class CCoagParticle extends Component { 32 | public final static Color COLOR = new Color(0.1f, 0.4f, 1.0f, 1.0f); 33 | 34 | public CCoagParticle() { 35 | super(Game.engine.makeCircle()); 36 | double x = -CBounds.PAD + Math.random()*(GameEngine.SCREEN_WIDTH + 2*CBounds.PAD); 37 | double y = -CBounds.PAD + Math.random()*(GameEngine.SCREEN_HEIGHT + 2*CBounds.PAD); 38 | HBCircle circ = circ(); 39 | circ.setPos(x, y); 40 | circ.setVel(1000*(.5 - Math.random()), 1000*(.5 - Math.random())); 41 | circ.setDiam(3); 42 | circ.commit(Double.POSITIVE_INFINITY); 43 | if(!isInBounds()) throw new RuntimeException(); 44 | } 45 | @Override public boolean canInteract(Component other) { 46 | return other instanceof CCoagParticle || other instanceof CBounds; 47 | } 48 | @Override public boolean interactsWithBullets() {return false;} 49 | 50 | @Override public void onCollide(Component other) { 51 | if(isDeleted()) return; 52 | if(other instanceof CCoagParticle) { 53 | HBCircle circ = circ(); 54 | HBCircle oCirc = other.circ(); 55 | double area = Geom.area(circ); 56 | double oArea = Geom.area(oCirc); 57 | double newArea = area + oArea; 58 | circ.setDiam(Geom.area2Diam(newArea)); 59 | circ.setVelX(circ.getVelX()*area/newArea + oCirc.getVelX()*oArea/newArea); 60 | circ.setVelY(circ.getVelY()*area/newArea + oCirc.getVelY()*oArea/newArea); 61 | circ.setX(circ.getX()*area/newArea + oCirc.getX()*oArea/newArea); 62 | circ.setY(circ.getY()*area/newArea + oCirc.getY()*oArea/newArea); 63 | circ.commit(Double.POSITIVE_INFINITY); 64 | ((CCoagParticle)other).delete(); 65 | if(circ.getDiam() > 2*CBounds.PAD) { 66 | new CCircFade(circ, COLOR, .3, null); 67 | delete(); 68 | } 69 | } 70 | } 71 | @Override public void onSeparate(Component other) { 72 | if(other instanceof CBounds) { 73 | HBCircle circ = circ(); 74 | HBRect rect = other.rect(); 75 | double r = rect.getX() + .5*rect.getWidth(); 76 | double t = rect.getY() + .5*rect.getHeight(); 77 | double l = rect.getX() - .5*rect.getWidth(); 78 | double b = rect.getY() - .5*rect.getHeight(); 79 | if(circ.getX() < l) circ.setX(r); 80 | else if(circ.getX() > r) circ.setX(l); 81 | if(circ.getY() < b) circ.setY(t); 82 | else if(circ.getY() > t) circ.setY(b); 83 | circ.commit(Double.POSITIVE_INFINITY); 84 | if(!isInBounds()) throw new RuntimeException(); 85 | } 86 | } 87 | @Override public Color getColor() {return COLOR;} 88 | } 89 | -------------------------------------------------------------------------------- /.idea/inspectionProfiles/Project_Default.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 52 | -------------------------------------------------------------------------------- /core/src_gdx_util/com/matthewmichelotti/collider/Pool.java: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright 2011 See AUTHORS file. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | ******************************************************************************/ 16 | //!!!NOTICE!!! this file has been modified by Matthew Michelotti. 17 | //This file was originally part of the libGDX library 18 | //(https://github.com/libgdx/libgdx) under the Apache License as described 19 | //above. The AUTHORS file specified above can be found at 20 | //https://github.com/libgdx/libgdx/blob/master/AUTHORS . 21 | //The contents of the authors file is as follows: 22 | // 23 | // # This is the official list of the AUTHORS of libgdx 24 | // # for copyright purposes. 25 | // # This file is distinct from the CONTRIBUTORS files. 26 | // # See the latter for an explanation. 27 | // 28 | // # Names should be added to this file as 29 | // # Name or Organization 30 | // # The email address is not required for organizations. 31 | // Mario Zechner 32 | // Nathan Sweet 33 | // 34 | //End of AUTHORS file. 35 | // 36 | //All changes to this file are marked with comments starting with 37 | //my initials (MM). 38 | 39 | 40 | //MM: changed package name 41 | //package com.badlogic.gdx.utils; 42 | package com.matthewmichelotti.collider; 43 | 44 | /** A pool of objects that can be reused to avoid allocation. 45 | * @author Nathan Sweet */ 46 | //MM: changed class from public to package-private 47 | abstract class Pool { 48 | /** The maximum number of objects that will be pooled. */ 49 | public final int max; 50 | /** The highest number of free objects. Can be reset any time. */ 51 | public int peak; 52 | 53 | private final Array freeObjects; 54 | 55 | /** Creates a pool with an initial capacity of 16 and no maximum. */ 56 | public Pool () { 57 | this(16, Integer.MAX_VALUE); 58 | } 59 | 60 | /** Creates a pool with the specified initial capacity and no maximum. */ 61 | public Pool (int initialCapacity) { 62 | this(initialCapacity, Integer.MAX_VALUE); 63 | } 64 | 65 | /** @param max The maximum number of free objects to store in this pool. */ 66 | @SuppressWarnings("unchecked") 67 | public Pool (int initialCapacity, int max) { 68 | freeObjects = new Array(false, initialCapacity); 69 | this.max = max; 70 | } 71 | 72 | abstract protected T newObject (); 73 | 74 | /** Returns an object from this pool. The object may be new (from {@link #newObject()}) or reused (previously 75 | * {@link #free(Object) freed}). */ 76 | public T obtain () { 77 | return freeObjects.size == 0 ? newObject() : freeObjects.pop(); 78 | } 79 | 80 | /** Puts the specified object in the pool, making it eligible to be returned by {@link #obtain()}. If the pool already contains 81 | * {@link #max} free objects, the specified object is reset but not added to the pool. */ 82 | public void free (T object) { 83 | if (object == null) throw new IllegalArgumentException("object cannot be null."); 84 | if (freeObjects.size < max) { 85 | freeObjects.add(object); 86 | peak = Math.max(peak, freeObjects.size); 87 | } 88 | if (object instanceof Poolable) ((Poolable)object).reset(); 89 | } 90 | 91 | /** Puts the specified objects in the pool. Null objects within the array are silently ignored. 92 | * @see #free(Object) */ 93 | public void freeAll (Array objects) { 94 | if (objects == null) throw new IllegalArgumentException("object cannot be null."); 95 | Array freeObjects = this.freeObjects; 96 | int max = this.max; 97 | for (int i = 0; i < objects.size; i++) { 98 | T object = objects.get(i); 99 | if (object == null) continue; 100 | if (freeObjects.size < max) freeObjects.add(object); 101 | if (object instanceof Poolable) ((Poolable)object).reset(); 102 | } 103 | peak = Math.max(peak, freeObjects.size); 104 | } 105 | 106 | /** Removes all free objects from this pool. */ 107 | public void clear () { 108 | freeObjects.clear(); 109 | } 110 | 111 | /** The number of objects available to be obtained. */ 112 | public int getFree () { 113 | return freeObjects.size; 114 | } 115 | 116 | /** Objects implementing this interface will have {@link #reset()} called when passed to {@link #free(Object)}. */ 117 | static public interface Poolable { 118 | /** Resets the object for reuse. Object references should be nulled and fields may be set to default values. */ 119 | public void reset (); 120 | } 121 | } 122 | -------------------------------------------------------------------------------- /core/src/com/matthewmichelotti/collider/HBPositioned.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2013-2014 Matthew D. Michelotti 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.matthewmichelotti.collider; 18 | 19 | /** 20 | * Superclass of all HitBoxes that make use of a central position. 21 | * Currently, all concrete HitBox classes extend this class. 22 | * @author Matthew Michelotti 23 | */ 24 | public abstract class HBPositioned extends HitBox { 25 | double startX, startY; 26 | double velX, velY; 27 | 28 | HBPositioned(Collider collider) { 29 | super(collider); 30 | } 31 | 32 | @Override 33 | void init() { 34 | super.init(); 35 | this.startX = 0.0; 36 | this.startY = 0.0; 37 | this.velX = 0.0; 38 | this.velY = 0.0; 39 | } 40 | 41 | @Override 42 | void markTransitionStart() { 43 | double time = collider.getTime(); 44 | startX = getX(time); 45 | startY = getY(time); 46 | super.markTransitionStart(); 47 | } 48 | 49 | /** 50 | * Set the center x-coordinate. 51 | * @param x Center x-corrdinate. 52 | */ 53 | public final void setX(double x) { 54 | collider.altering(this); 55 | this.startX = x; 56 | } 57 | 58 | /** 59 | * Set the center y-coordinate. 60 | * @param y Center y-coordinate. 61 | */ 62 | public final void setY(double y) { 63 | collider.altering(this); 64 | this.startY = y; 65 | } 66 | 67 | /** 68 | * Set the velocity of the center x-coordinate. 69 | * @param velX Velocity of the center x-coordinate. 70 | */ 71 | public final void setVelX(double velX) { 72 | collider.altering(this); 73 | this.velX = velX; 74 | } 75 | 76 | /** 77 | * Set the velocity of the center y-coordinate. 78 | * @param velY Velocity of the center y-coordinate. 79 | */ 80 | public final void setVelY(double velY) { 81 | collider.altering(this); 82 | this.velY = velY; 83 | } 84 | 85 | /** 86 | * Set the center position. 87 | * @param x Center x-coordinate. 88 | * @param y Center y-coordinate. 89 | */ 90 | public final void setPos(double x, double y) { 91 | collider.altering(this); 92 | this.startX = x; 93 | this.startY = y; 94 | } 95 | 96 | /** 97 | * Set the velocity of the center position. 98 | * @param velX Velocity of the center x-coordinate. 99 | * @param velY Velocity of the center y-coordinate. 100 | */ 101 | public final void setVel(double velX, double velY) { 102 | collider.altering(this); 103 | this.velX = velX; 104 | this.velY = velY; 105 | } 106 | 107 | final double getX(double time) {return startX + (time - startTime)*velX;} 108 | final double getY(double time) {return startY + (time - startTime)*velY;} 109 | 110 | /** 111 | * Get the center x-coordinate. 112 | * @return The center x-coordinate. 113 | */ 114 | public final double getX() {return getX(collider.getTime());} 115 | 116 | /** 117 | * Get the center y-coordinate. 118 | * @return The center y-coordinate. 119 | */ 120 | public final double getY() {return getY(collider.getTime());} 121 | 122 | /** 123 | * Get the velocity of the center x-coordinate. 124 | * @return The velocity of the center x-coordinate. 125 | */ 126 | public final double getVelX() {return velX;} 127 | 128 | /** 129 | * Get the velocity of the center y-coordinate. 130 | * @return The velocity of the center y-coordinate. 131 | */ 132 | public final double getVelY() {return velY;} 133 | 134 | final double getStartPosComp(int dir) { 135 | switch(dir) { 136 | case Dir.R: return startX; 137 | case Dir.U: return startY; 138 | case Dir.L: return -startX; 139 | case Dir.D: return -startY; 140 | default: throw new IllegalArgumentException(); 141 | } 142 | } 143 | 144 | final double getVelComp(int dir) { 145 | switch(dir) { 146 | case Dir.R: return velX; 147 | case Dir.U: return velY; 148 | case Dir.L: return -velX; 149 | case Dir.D: return -velY; 150 | default: throw new IllegalArgumentException(); 151 | } 152 | } 153 | 154 | final double getPosComp(int dir, double time) { 155 | return getStartPosComp(dir) + (time - startTime)*getVelComp(dir); 156 | } 157 | 158 | final void dummySetStartCoord(int dir, double value) { 159 | switch(dir) { 160 | case Dir.R: startX = value; return; 161 | case Dir.U: startY = value; return; 162 | default: throw new IllegalArgumentException(); 163 | } 164 | } 165 | 166 | final void dummySetVelCoord(int dir, double value) { 167 | switch(dir) { 168 | case Dir.R: velX = value; return; 169 | case Dir.U: velY = value; return; 170 | default: throw new IllegalArgumentException(); 171 | } 172 | } 173 | } 174 | -------------------------------------------------------------------------------- /demos-core/src/com/matthewmichelotti/collider/demos/comps/CElastic.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2013-2014 Matthew D. Michelotti 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.matthewmichelotti.collider.demos.comps; 18 | 19 | import com.badlogic.gdx.graphics.Color; 20 | import com.badlogic.gdx.utils.Array; 21 | import com.badlogic.gdx.utils.ObjectSet; 22 | import com.matthewmichelotti.collider.HBCircle; 23 | import com.matthewmichelotti.collider.Normal; 24 | import com.matthewmichelotti.collider.demos.Component; 25 | import com.matthewmichelotti.collider.demos.Game; 26 | import com.matthewmichelotti.collider.demos.Geom; 27 | 28 | /** 29 | * A circular object that undergoes perfectly elastic collisions. 30 | * @author Matthew Michelotti 31 | */ 32 | public class CElastic extends Component { 33 | private final static Color[] COLORS = new Color[] { 34 | new Color(1.0f, 1.0f, 0.0f, 1.0f), 35 | new Color(0.0f, 0.0f, 1.0f, 1.0f), 36 | new Color(1.0f, 0.0f, 0.0f, 1.0f), 37 | new Color(0.5f, 0.0f, 0.8f, 1.0f), 38 | new Color(1.0f, 0.5f, 0.0f, 1.0f), 39 | new Color(0.0f, 0.35f, 0.0f, 1.0f), 40 | new Color(0.6f, 0.0f, 0.0f, 1.0f), 41 | new Color(0.0f, 0.0f, 0.0f, 1.0f) 42 | }; 43 | 44 | public final static int NUM_COLORS = 2*COLORS.length - 1; 45 | 46 | private Color color; 47 | private Array overlaps = new Array(); 48 | 49 | public CElastic(double x, double y, double diam, double maxVel, int colorI) { 50 | super(Game.engine.makeCircle()); 51 | this.color = COLORS[colorI % COLORS.length]; 52 | HBCircle circ = circ(); 53 | circ.setPos(x, y); 54 | circ.setDiam(diam); 55 | circ.setVel(2*maxVel*(.5 - Math.random()), 2*maxVel*(.5 - Math.random())); 56 | circ.commit(Double.POSITIVE_INFINITY); 57 | } 58 | 59 | @Override public boolean canInteract(Component other) { 60 | return other instanceof CElastic || other instanceof CTarget; 61 | } 62 | @Override public boolean interactsWithBullets() {return false;} 63 | 64 | @Override 65 | public void onCollide(Component other) { 66 | CElastic otherCE = null; 67 | if(other instanceof CElastic) otherCE = (CElastic)other; 68 | if(otherCE != null && getId() > otherCE.getId()) return; 69 | boolean success = elasticCollision(other); 70 | overlaps.add(other); 71 | if(otherCE != null) otherCE.overlaps.add(this); 72 | if(!success) return; 73 | if(overlaps.size == 1 && (otherCE == null || otherCE.overlaps.size == 1)) return; 74 | ObjectSet visitedSet = new ObjectSet(); 75 | for(int i = 0; i < 100; i++) { 76 | if(!collideIteration(visitedSet)) { 77 | if(i >= 15) { 78 | System.out.println("WARNING: chained elastic collision took " 79 | + (i + 1) + " iterations"); 80 | } 81 | return; 82 | } 83 | visitedSet.clear(); 84 | } 85 | throw new RuntimeException("chained elastic collision not converging"); 86 | } 87 | 88 | @Override 89 | public void onSeparate(Component other) { 90 | overlaps.removeValue(other, true); 91 | } 92 | 93 | @Override public Color getColor() {return color;} 94 | 95 | private boolean elasticCollision(Component other) { 96 | if(other instanceof CTarget) { 97 | Normal normal = other.hitBox().getNormal(hitBox()); 98 | boolean success = elasticCollision(normal.getUnitX(), normal.getUnitY(), 0, 0, 99 | Double.POSITIVE_INFINITY); 100 | if(success) ((CTarget)other).hit(); 101 | return success; 102 | } 103 | else if(other instanceof CElastic) { 104 | HBCircle circA = circ(); 105 | HBCircle circB = other.circ(); 106 | Normal n = circB.getNormal(circA); 107 | double v1x = circA.getVelX(); 108 | double v1y = circA.getVelY(); 109 | double v2x = circB.getVelX(); 110 | double v2y = circB.getVelY(); 111 | boolean result = elasticCollision( 112 | n.getUnitX(), n.getUnitY(), v2x, v2y, Geom.area(circB)); 113 | result |= ((CElastic)other).elasticCollision( 114 | -n.getUnitX(), -n.getUnitY(), v1x, v1y, Geom.area(circA)); 115 | 116 | // new CCircFade(circB.getX() + .5*n.getUnitX()*circB.getDiam(), 117 | // circB.getY() + .5*n.getUnitY()*circB.getDiam(), 118 | // 5, Color.WHITE, .2, null); 119 | 120 | return result; 121 | } 122 | throw new RuntimeException(); 123 | } 124 | 125 | private boolean elasticCollision(double nx, double ny, double v2x, double v2y, double m2) { 126 | HBCircle circ = circ(); 127 | double m1 = Geom.area(circ); 128 | double v1x = circ.getVelX(); 129 | double v1y = circ.getVelY(); 130 | 131 | double normalRelVelComp = nx*(v2x - v1x) + ny*(v2y - v1y); 132 | if(normalRelVelComp <= 0.00001) return false; 133 | double massRatio; 134 | if(m2 == Double.POSITIVE_INFINITY) massRatio = 1.0; 135 | else massRatio = m2/(m1 + m2); 136 | double term = 2*massRatio*normalRelVelComp; 137 | circ.setVel(v1x + term*nx, v1y + term*ny); 138 | circ.commit(Double.POSITIVE_INFINITY); 139 | return true; 140 | } 141 | 142 | private boolean collideIteration(ObjectSet visitedSet) { 143 | visitedSet.add(this); 144 | boolean changed = false; 145 | for(Component c : overlaps) { 146 | if(visitedSet.contains(c)) continue; 147 | changed |= elasticCollision(c); 148 | } 149 | for(Component c : overlaps) { 150 | if(c instanceof CElastic) { 151 | if(visitedSet.contains(c)) continue; 152 | changed |= ((CElastic)c).collideIteration(visitedSet); 153 | } 154 | } 155 | return changed; 156 | } 157 | } 158 | -------------------------------------------------------------------------------- /core/src/com/matthewmichelotti/collider/HBRect.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2013-2014 Matthew D. Michelotti 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.matthewmichelotti.collider; 18 | 19 | /** 20 | * An axis-aligned rectangular HitBox. 21 | * @author Matthew Michelotti 22 | * @see Collider#makeRect() 23 | */ 24 | public final class HBRect extends HBPositioned { 25 | double startHW, startHH; 26 | double velHW, velHH; 27 | 28 | HBRect(Collider collider) { 29 | super(collider); 30 | } 31 | 32 | @Override 33 | void init() { 34 | super.init(); 35 | this.startHW = 0.0; 36 | this.startHH = 0.0; 37 | this.velHW = 0.0; 38 | this.velHH = 0.0; 39 | } 40 | 41 | @Override 42 | void markTransitionStart() { 43 | double time = collider.getTime(); 44 | startHW = getHW(time); 45 | startHH = getHH(time); 46 | super.markTransitionStart(); 47 | } 48 | 49 | @Override 50 | public void free() { 51 | collider.free(this); 52 | super.free(); 53 | } 54 | 55 | /** 56 | * Set the width. 57 | * @param width Width. 58 | */ 59 | public void setWidth(double width) { 60 | collider.altering(this); 61 | this.startHW = .5*width; 62 | } 63 | 64 | /** 65 | * Set the height. 66 | * @param height Height. 67 | */ 68 | public void setHeight(double height) { 69 | collider.altering(this); 70 | this.startHH = .5*height; 71 | } 72 | 73 | /** 74 | * Set the velocity of the width. 75 | * @param velWidth Velocity of the width. 76 | */ 77 | public void setVelWidth(double velWidth) { 78 | collider.altering(this); 79 | this.velHW = .5*velWidth; 80 | } 81 | 82 | /** 83 | * Set the velocity of the height. 84 | * @param velHeight Velocity of the height. 85 | */ 86 | public void setVelHeight(double velHeight) { 87 | collider.altering(this); 88 | this.velHH = .5*velHeight; 89 | } 90 | 91 | 92 | /** 93 | * Set the width and height to the same value. 94 | * @param dim Width and height. 95 | */ 96 | public void setDims(double dim) { 97 | collider.altering(this); 98 | this.startHW = .5*dim; 99 | this.startHH = startHW; 100 | } 101 | 102 | /** 103 | * Set the width and height. 104 | * @param width Width. 105 | * @param height Height. 106 | */ 107 | public void setDims(double width, double height) { 108 | collider.altering(this); 109 | this.startHW = .5*width; 110 | this.startHH = .5*height; 111 | } 112 | 113 | /** 114 | * Set the velocities of the width and height to the same value. 115 | * @param velDim Velocity of the width/height. 116 | */ 117 | public void setVelDims(double velDim) { 118 | collider.altering(this); 119 | this.velHW = .5*velDim; 120 | this.velHH = velHW; 121 | } 122 | 123 | /** 124 | * Set the velocities of the width and height; 125 | * @param velWidth Velocity of the width. 126 | * @param velHeight Velocity of the height. 127 | */ 128 | public void setVelDims(double velWidth, double velHeight) { 129 | collider.altering(this); 130 | this.velHW = .5*velWidth; 131 | this.velHH = .5*velHeight; 132 | } 133 | 134 | double getHW(double time) {return startHW + (time - startTime)*velHW;} 135 | double getHH(double time) {return startHH + (time - startTime)*velHH;} 136 | 137 | /** 138 | * Get the width. 139 | * @return Width. 140 | */ 141 | public double getWidth() {return 2*getHW(collider.getTime());} 142 | 143 | /** 144 | * Get the height. 145 | * @return Height. 146 | */ 147 | public double getHeight() {return 2*getHH(collider.getTime());} 148 | 149 | /** 150 | * Get the velocity of the width. 151 | * @return Velocity of the width. 152 | */ 153 | public double getVelWidth() {return 2*velHW;} 154 | 155 | /** 156 | * Get the velocity of the height. 157 | * @return Velocity of the height. 158 | */ 159 | public double getVelHeight() {return 2*velHH;} 160 | 161 | double getStartHDim(int dir) { 162 | switch(dir) { 163 | case Dir.R: case Dir.L: return startHW; 164 | case Dir.U: case Dir.D: return startHH; 165 | default: throw new IllegalArgumentException(); 166 | } 167 | } 168 | 169 | double getVelHDim(int dir) { 170 | switch(dir) { 171 | case Dir.R: case Dir.L: return velHW; 172 | case Dir.U: case Dir.D: return velHH; 173 | default: throw new IllegalArgumentException(); 174 | } 175 | } 176 | 177 | double getStartEdgeComp(int edge) { 178 | return getStartPosComp(edge) + getStartHDim(edge); 179 | } 180 | 181 | double getVelEdgeComp(int edge) { 182 | return getVelComp(edge) + getVelHDim(edge); 183 | } 184 | 185 | double getEdgeComp(int edge, double time) { 186 | return getStartEdgeComp(edge) + (time - startTime)*getVelEdgeComp(edge); 187 | } 188 | 189 | @Override 190 | double getBoundEdgeComp(int edge, double startTime, double endTime) { 191 | double base = getStartEdgeComp(edge); 192 | double vel = getVelEdgeComp(edge); 193 | double evalTime = (vel > 0.0) ? endTime : startTime; 194 | return base + vel*(evalTime - this.startTime); 195 | } 196 | 197 | @Override 198 | boolean isMoving() { 199 | return velX != 0.0 || velY != 0.0 || velHW != 0.0 || velHH != 0.0; 200 | } 201 | 202 | @Override 203 | double getMaxBoundEdgeVel() { 204 | double vel = 0.0; 205 | for(int dir = 0; dir < 2; dir++) { 206 | vel = Math.max(vel, Arith.abs(getVelComp(dir)) + Arith.abs(getVelHDim(dir))); 207 | } 208 | return vel; 209 | } 210 | 211 | void dummyMimicCircle(HBCircle c) { 212 | startTime = c.startTime; 213 | startX = c.startX; 214 | startY = c.startY; 215 | startHW = c.startRad; 216 | startHH = c.startRad; 217 | velX = c.velX; 218 | velY = c.velY; 219 | velHW = c.velRad; 220 | velHH = c.velRad; 221 | } 222 | } 223 | -------------------------------------------------------------------------------- /core/src/com/matthewmichelotti/collider/Field.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2013-2014 Matthew D. Michelotti 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.matthewmichelotti.collider; 18 | 19 | import java.util.Iterator; 20 | import java.util.NoSuchElementException; 21 | 22 | final class Field { 23 | private SetPool setPool = new SetPool(); 24 | private LongMap data; 25 | private double cellWidth; 26 | 27 | private int numEntries = 0; 28 | 29 | private IntBox.Iterator boxIter = new IntBox.Iterator(); 30 | private IntBox.DiffIterator diffIter = new IntBox.DiffIterator(); 31 | 32 | private HitBoxIter iter = new HitBoxIter(); 33 | 34 | Field(ColliderOpts opts) { 35 | if(opts.cellWidth <= 0.0) throw new IllegalArgumentException(); 36 | cellWidth = opts.cellWidth; 37 | data = new LongMap(); 38 | } 39 | 40 | int getNumEntries() {return numEntries;} 41 | 42 | void remove(HitBox hitBox, int group, IntBox oldBox, IntBox newBox) { 43 | if(group < 0) return; 44 | Int2DIterator iter = iterator(oldBox, newBox); 45 | for(; !iter.isDone(); iter.next()) { 46 | removeFromCell(hitBox, iter.getX(), iter.getY(), group); 47 | } 48 | } 49 | 50 | void add(HitBox hitBox, int group, IntBox oldBox, IntBox newBox) { 51 | if(group < 0) return; 52 | Int2DIterator iter = iterator(newBox, oldBox); 53 | for(; !iter.isDone(); iter.next()) { 54 | addToCell(hitBox, iter.getX(), iter.getY(), group); 55 | } 56 | } 57 | 58 | //NOTE: should iterate to completion 59 | Iterable iterator(IntBox region, int[] groups, int testId) { 60 | iter.init(region, groups, testId); 61 | return iter; 62 | } 63 | 64 | void getIndexBounds(HitBox hitBox, IntBox bounds) { 65 | bounds.l = Arith.floor(-hitBox.getBoundEdgeComp(Dir.L)/cellWidth); 66 | bounds.b = Arith.floor(-hitBox.getBoundEdgeComp(Dir.D)/cellWidth); 67 | bounds.r = Arith.max(bounds.l, Arith.ceil(hitBox.getBoundEdgeComp(Dir.R)/cellWidth) - 1); 68 | bounds.t = Arith.max(bounds.b, Arith.ceil(hitBox.getBoundEdgeComp(Dir.U)/cellWidth) - 1); 69 | } 70 | 71 | double getGridPeriod(HitBox hitBox) { 72 | double speed = hitBox.getMaxBoundEdgeVel(); 73 | if(speed <= 0.0) return Double.POSITIVE_INFINITY; 74 | else return cellWidth/speed; 75 | } 76 | 77 | private void addToCell(HitBox hitBox, int x, int y, int group) { 78 | long key = getKey(x, y, group); 79 | Object oldSetObj = data.get(key); 80 | Object newSetObj = setPool.add(oldSetObj, hitBox); 81 | if(!setPool.wasSuccessful()) throw new RuntimeException(); 82 | if(newSetObj != oldSetObj) data.put(key, newSetObj); 83 | numEntries++; 84 | } 85 | 86 | private void removeFromCell(HitBox hitBox, int x, int y, int group) { 87 | long key = getKey(x, y, group); 88 | Object oldSetObj = data.get(key); 89 | Object newSetObj = setPool.remove(oldSetObj, hitBox); 90 | if(!setPool.wasSuccessful()) throw new RuntimeException(); 91 | if(newSetObj == null) data.remove(key); 92 | else if(newSetObj != oldSetObj) data.put(key, newSetObj); 93 | numEntries--; 94 | } 95 | 96 | private final static long PRIME = 160481219; //NOTE: PRIME*PRIME*SMALL_PRIME < 2^63 97 | private final static long SMALL_PRIME = 263; //NOTE: SMALL_PRIME > HitBox.NUM_GROUPS 98 | private final static int MAX_INDEX = (int)(PRIME/2 - 1); 99 | 100 | private static long getKey(int x, int y, int group) { 101 | //this key is used because, at the time of writing, the LongMap in 102 | //LibGDX just uses the lower bits of the key as the first hash function 103 | if(x > MAX_INDEX || x < -MAX_INDEX) throw new RuntimeException(); 104 | if(y > MAX_INDEX || y < -MAX_INDEX) throw new RuntimeException(); 105 | if(group >= SMALL_PRIME || group < 0) throw new RuntimeException(); 106 | return group + (SMALL_PRIME*x + PRIME*y); 107 | // return ((x & 0xFFFF0000L) << 32) | ((y & 0xFFFFFFFFL) << 16) | (x & 0xFFFFL); 108 | } 109 | 110 | private Int2DIterator iterator(IntBox box, IntBox subBox) { 111 | if(subBox == null) { 112 | boxIter.init(box); 113 | return boxIter; 114 | } 115 | else { 116 | diffIter.init(box, subBox); 117 | return diffIter; 118 | } 119 | } 120 | 121 | private class HitBoxIter implements Iterator, Iterable { 122 | private final IntBox.Iterator boxIter = new IntBox.Iterator(); 123 | private final SetPool.SetIterator cellIter = new SetPool.SetIterator(); 124 | private int[] groups; 125 | private int groupIndex; 126 | private HitBox next; 127 | private int testId = 0; 128 | 129 | private void init(IntBox region, int[] groups, int testId) { 130 | clear(); 131 | if(groups.length == 0) return; 132 | boxIter.init(region); 133 | if(boxIter.isDone()) return; 134 | 135 | this.testId = testId; 136 | this.groups = groups; 137 | initCellIter(); 138 | searchNext(); 139 | } 140 | 141 | private void searchNext() { 142 | while(true) { 143 | while(cellIter.hasNext()) { 144 | next = cellIter.next(); 145 | if(next.testMark(testId)) return; 146 | } 147 | groupIndex++; 148 | if(groupIndex >= groups.length) { 149 | groupIndex = 0; 150 | boxIter.next(); 151 | if(boxIter.isDone()) { 152 | clear(); 153 | return; 154 | } 155 | } 156 | initCellIter(); 157 | } 158 | } 159 | 160 | private void initCellIter() { 161 | long key = getKey(boxIter.getX(), boxIter.getY(), groups[groupIndex]); 162 | cellIter.init(data.get(key)); 163 | } 164 | 165 | private void clear() { 166 | cellIter.clear(); 167 | groups = null; 168 | groupIndex = 0; 169 | next = null; 170 | } 171 | 172 | @Override 173 | public HitBox next() { 174 | if(next == null) throw new NoSuchElementException(); 175 | HitBox result = next; 176 | searchNext(); 177 | return result; 178 | } 179 | 180 | @Override public Iterator iterator() {return this;} 181 | @Override public boolean hasNext() {return next != null;} 182 | @Override public void remove() {throw new UnsupportedOperationException();} 183 | } 184 | } 185 | -------------------------------------------------------------------------------- /demos-core/src/com/matthewmichelotti/collider/demos/Scenarios.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2013-2014 Matthew D. Michelotti 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.matthewmichelotti.collider.demos; 18 | 19 | import com.badlogic.gdx.graphics.Color; 20 | import com.matthewmichelotti.collider.HBRect; 21 | import com.matthewmichelotti.collider.demos.comps.CBounds; 22 | import com.matthewmichelotti.collider.demos.comps.CCoagParticle; 23 | import com.matthewmichelotti.collider.demos.comps.CElastic; 24 | import com.matthewmichelotti.collider.demos.comps.CEnemyShip; 25 | import com.matthewmichelotti.collider.demos.comps.CIndicator; 26 | import com.matthewmichelotti.collider.demos.comps.CMorphEnemyShip; 27 | import com.matthewmichelotti.collider.demos.comps.CMorphStickySpinner; 28 | import com.matthewmichelotti.collider.demos.comps.CPlayerShip; 29 | import com.matthewmichelotti.collider.demos.comps.CStickyGun; 30 | import com.matthewmichelotti.collider.demos.comps.CTarget; 31 | import com.matthewmichelotti.collider.demos.comps.CText; 32 | import com.matthewmichelotti.collider.demos.comps.CVarietyGun; 33 | 34 | /** 35 | * Contains static methods for initializing each scenario. 36 | * @author Matthew Michelotti 37 | */ 38 | public class Scenarios { 39 | public final static int NUM_SCENARIOS = 11; 40 | 41 | private Scenarios() {} 42 | 43 | public static void initScenario(int i) { 44 | Game.engine.clear(); 45 | switch(i) { 46 | case 0: initInstructions(); break; 47 | case 1: initDanmaku1(); break; 48 | case 2: initDanmaku2(); break; 49 | case 3: initPool1(); break; 50 | case 4: initPool2(3); break; 51 | case 5: initPool2(40); break; 52 | case 6: initFractal(11); break; 53 | case 7: initFractal(3); break; 54 | case 8: initCoagulation(); break; 55 | case 9: initSpinners(); break; 56 | case 10: initIndicators(); break; 57 | default: throw new RuntimeException(); 58 | } 59 | } 60 | 61 | private static void initInstructions() { 62 | new CText("click screen each time you want to proceed to next demo", 640, 360); 63 | } 64 | 65 | private static void initDanmaku1() { 66 | Game.engine.setBG(new Color(0.0f, 0.02f, 0.05f, 1.0f)); 67 | new CBounds(); 68 | new CPlayerShip(); 69 | new CEnemyShip(640, 650); 70 | new CEnemyShip(340, 500); 71 | new CEnemyShip(940, 500); 72 | } 73 | 74 | private static void initDanmaku2() { 75 | Game.engine.setBG(new Color(0.0f, 0.02f, 0.05f, 1.0f)); 76 | new CBounds(); 77 | new CPlayerShip(); 78 | new CMorphEnemyShip(640, 650); 79 | new CMorphEnemyShip(340, 500); 80 | new CMorphEnemyShip(940, 500); 81 | } 82 | 83 | private static void initCoagulation() { 84 | Game.engine.setBG(new Color(.95f, 0.98f, 1.0f, 1.0f)); 85 | new CBounds(); 86 | Game.engine.addEvent(new FunctionEvent() { 87 | @Override public void resolve() { 88 | for(int i = 0; i < 150; i++) { 89 | new CCoagParticle(); 90 | } 91 | setTime(getTime() + .1); 92 | Game.engine.addEvent(this); 93 | } 94 | }); 95 | } 96 | 97 | private static void initPool1() { 98 | makePoolBorder(); 99 | for(int i = 0; i < CElastic.NUM_COLORS; i++) { //40 or 600 100 | double x = 56 + Math.random()*(1280 - 2*56); 101 | double y = 56 + Math.random()*(720 - 2*56); 102 | new CElastic(x, y, 40, 500, i); 103 | } 104 | } 105 | 106 | private static void initPool2(int numSets) { 107 | makePoolBorder(); 108 | for(int i = 0; i < numSets*CElastic.NUM_COLORS; i++) { //40 or 600 109 | double x = 56 + Math.random()*(1280 - 2*56); 110 | double y = 56 + Math.random()*(720 - 2*56); 111 | double diam = 1.15*(5 + Math.min(25, -Math.log(Math.random())*9)); 112 | new CElastic(x, y, diam, 250, i % CElastic.NUM_COLORS); 113 | } 114 | } 115 | 116 | private static void initFractal(double shotDiam) { 117 | Game.engine.setBG(new Color(0.0f, 0.02f, 0.05f, 1.0f)); 118 | new CBounds(); 119 | HBRect rect = Game.engine.makeRect(); 120 | rect.setPos(640, 360); 121 | rect.setDims(80); 122 | rect.commit(Double.POSITIVE_INFINITY); 123 | new CTarget(rect, CPlayerShip.COLOR); 124 | for(int i = 0; i < 8; i++) { 125 | double angle = 2*Math.PI*i/8.0; 126 | double cos = Math.cos(angle); 127 | double sin = Math.sin(angle); 128 | new CStickyGun(640 + cos*350, 360 + sin*350, angle + Math.PI, shotDiam); 129 | } 130 | } 131 | 132 | private static void initSpinners() { 133 | Game.engine.setBG(new Color(0.0f, 0.02f, 0.05f, 1.0f)); 134 | new CBounds(); 135 | new CMorphStickySpinner(640 - 200, 360 - 100); 136 | new CMorphStickySpinner(640 + 200, 360 + 100); 137 | } 138 | 139 | private static void initIndicators() { 140 | Game.engine.setBG(new Color(0.0f, 0.02f, 0.05f, 1.0f)); 141 | new CBounds(); 142 | new CVarietyGun(50, 50, .25*Math.PI); 143 | new CVarietyGun(1280 - 50, 50, .75*Math.PI); 144 | new CVarietyGun(50, 720 - 50, -.25*Math.PI); 145 | new CVarietyGun(1280 - 50, 720 - 50, -.75*Math.PI); 146 | new CIndicator(640 - 300, 360, true); 147 | new CIndicator(640 + 300, 360, false); 148 | } 149 | 150 | private static void makePoolBorder() { 151 | Game.engine.setBG(new Color(0.0f, 0.7f, 0.2f, 1.0f)); 152 | Color color = new Color(0.4f, 0.25f, 0.1f, 1.0f); 153 | 154 | for(int x = 0; x < 1280; x += 80) { 155 | HBRect rect = Game.engine.makeRect(); 156 | rect.setPos(x + 40, 20); 157 | rect.setDims(80, 40); 158 | rect.commit(Double.POSITIVE_INFINITY); 159 | new CTarget(rect, color); 160 | rect = Game.engine.makeRect(); 161 | rect.setPos(x + 40, 720 - 20); 162 | rect.setDims(80, 40); 163 | rect.commit(Double.POSITIVE_INFINITY); 164 | new CTarget(rect, color); 165 | } 166 | 167 | for(int y = 40; y < 720 - 40; y += 80) { 168 | HBRect rect = Game.engine.makeRect(); 169 | rect.setPos(20, y + 40); 170 | rect.setDims(40, 80); 171 | rect.commit(Double.POSITIVE_INFINITY); 172 | new CTarget(rect, color); 173 | rect = Game.engine.makeRect(); 174 | rect.setPos(1280 - 20, y + 40); 175 | rect.setDims(40, 80); 176 | rect.commit(Double.POSITIVE_INFINITY); 177 | new CTarget(rect, color); 178 | } 179 | } 180 | } 181 | -------------------------------------------------------------------------------- /core/src/com/matthewmichelotti/collider/SetPool.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2013-2014 Matthew D. Michelotti 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.matthewmichelotti.collider; 18 | 19 | import java.util.Iterator; 20 | import java.util.NoSuchElementException; 21 | 22 | final class SetPool { 23 | private int arrayCap; 24 | private int arrayReturnThreshold; 25 | private int setInitCap; 26 | 27 | private Pool arrayPool; 28 | private Pool> setPool; 29 | 30 | private SetIterator iterator = new SetIterator(); 31 | 32 | private boolean lastOpSuccess; 33 | 34 | SetPool() { 35 | this(12, 32); 36 | } 37 | 38 | SetPool(int arrayCap) { 39 | this(arrayCap, 32); 40 | } 41 | 42 | SetPool(int arrayCap, int setInitCap) { 43 | if(arrayCap < 1) throw new IllegalArgumentException(); 44 | if(setInitCap <= arrayCap) throw new IllegalArgumentException(); 45 | this.arrayCap = arrayCap; 46 | this.arrayReturnThreshold = (arrayCap + 1)/2; 47 | this.setInitCap = setInitCap; 48 | 49 | arrayPool = new Pool() { 50 | @Override protected Object[] newObject() { 51 | return new Object[SetPool.this.arrayCap]; 52 | } 53 | }; 54 | 55 | setPool = new Pool>() { 56 | @Override protected ObjectSet newObject() { 57 | return new ObjectSet(SetPool.this.setInitCap); 58 | } 59 | }; 60 | } 61 | 62 | @SuppressWarnings("unchecked") 63 | Object add(Object setObj, T value) { 64 | if(value == null) throw new IllegalArgumentException(); 65 | lastOpSuccess = true; 66 | Class valueClass = value.getClass(); 67 | if(valueClass == Object[].class || valueClass == ObjectSet.class) { 68 | throw new IllegalArgumentException(); 69 | } 70 | if(setObj == null) return value; 71 | Class setObjClass = setObj.getClass(); 72 | if(setObjClass == Object[].class) { 73 | Object[] arr = (Object[])setObj; 74 | int size; 75 | for(size = 0; size < arrayCap && arr[size] != null; size++) { 76 | if(arr[size] == value) { 77 | lastOpSuccess = false; 78 | return arr; 79 | } 80 | } 81 | if(size != arrayCap) { 82 | arr[size] = value; 83 | return arr; 84 | } 85 | ObjectSet set = setPool.obtain(); 86 | for(int i = 0; i < arrayCap; i++) { 87 | set.add(arr[i]); 88 | arr[i] = null; 89 | } 90 | set.add(value); 91 | arrayPool.free(arr); 92 | return set; 93 | } 94 | if(setObjClass == ObjectSet.class) { 95 | lastOpSuccess = ((ObjectSet)setObj).add(value); 96 | return setObj; 97 | } 98 | if(setObj == value) { 99 | lastOpSuccess = false; 100 | return setObj; 101 | } 102 | Object[] arr = arrayPool.obtain(); 103 | arr[0] = setObj; 104 | arr[1] = value; 105 | return arr; 106 | } 107 | 108 | @SuppressWarnings("unchecked") 109 | Object clear(Object setObj) { 110 | if(setObj == null) return null; 111 | Class setObjClass = setObj.getClass(); 112 | if(setObjClass == Object[].class) { 113 | Object[] arr = (Object[])setObj; 114 | for(int i = 0; i < arr.length && arr[i] != null; i++) { 115 | arr[i] = null; 116 | } 117 | arrayPool.free(arr); 118 | } 119 | else if(setObjClass == ObjectSet.class) { 120 | ObjectSet set = (ObjectSet)setObj; 121 | set.clear(setInitCap); 122 | setPool.free(set); 123 | } 124 | return null; 125 | } 126 | 127 | @SuppressWarnings("unchecked") 128 | Object remove(Object setObj, T value) { 129 | if(value == null) throw new IllegalArgumentException(); 130 | lastOpSuccess = true; 131 | if(setObj == null) { 132 | lastOpSuccess = false; 133 | return null; 134 | } 135 | Class setObjClass = setObj.getClass(); 136 | if(setObjClass == Object[].class) { 137 | Object[] arr = (Object[])setObj; 138 | int i, size; 139 | for(i = 0; i < arrayCap; i++) { 140 | Object elem = arr[i]; 141 | if(elem == null) { 142 | lastOpSuccess = false; 143 | return setObj; 144 | } 145 | if(elem == value) break; 146 | } 147 | if(i == arrayCap) return setObj; 148 | for(size = i + 1; size < arrayCap && arr[size] != null; size++); 149 | arr[i] = arr[size - 1]; 150 | arr[size - 1] = null; 151 | return shrinkArray(arr); 152 | } 153 | else if(setObjClass == ObjectSet.class) { 154 | ObjectSet set = (ObjectSet)setObj; 155 | lastOpSuccess = set.remove(value); 156 | if(!lastOpSuccess) return set; 157 | return shrinkSet(set); 158 | } 159 | else { 160 | if(setObj == value) return null; 161 | lastOpSuccess = false; 162 | return setObj; 163 | } 164 | } 165 | 166 | boolean wasSuccessful() {return lastOpSuccess;} 167 | 168 | SetIterator iterator(Object setObj) { 169 | iterator.init(setObj); 170 | return iterator; 171 | } 172 | 173 | private Object shrinkArray(Object[] arr) { 174 | if(arr[1] != null) return arr; 175 | Object value = arr[0]; 176 | arr[0] = null; 177 | arrayPool.free(arr); 178 | return value; 179 | } 180 | 181 | private Object shrinkSet(ObjectSet set) { 182 | if(set.size > arrayReturnThreshold) return set; 183 | Object result; 184 | if(set.size == 0) result = null; 185 | else if(set.size == 1) result = set.iterator().next(); 186 | else { 187 | Object[] arr = arrayPool.obtain(); 188 | int i = 0; 189 | for(Object o : set) { 190 | arr[i] = o; 191 | i++; 192 | } 193 | result = arr; 194 | } 195 | set.clear(setInitCap); 196 | setPool.free(set); 197 | return result; 198 | } 199 | 200 | static class SetIterator implements Iterator, Iterable { 201 | private Object value; 202 | private Object[] arr; 203 | private Iterator setIter; 204 | private int index; 205 | 206 | SetIterator() {} 207 | 208 | SetIterator(Object setObj) {init(setObj);} 209 | 210 | @SuppressWarnings("unchecked") 211 | void init(Object setObj) { 212 | clear(); 213 | if(setObj == null) return; 214 | Class setObjClass = setObj.getClass(); 215 | if(setObjClass == Object[].class) { 216 | arr = (Object[])setObj; 217 | if(arr[0] == null) arr = null; 218 | } 219 | else if(setObjClass == ObjectSet.class) { 220 | ObjectSet set = (ObjectSet)setObj; 221 | if(set.size != 0) setIter = ((ObjectSet)setObj).iterator(); 222 | } 223 | else { 224 | value = setObj; 225 | } 226 | } 227 | 228 | void clear() { 229 | value = null; 230 | arr = null; 231 | setIter = null; 232 | index = 0; 233 | } 234 | 235 | @Override 236 | public boolean hasNext() { 237 | return value != null || arr != null || setIter != null; 238 | } 239 | 240 | @SuppressWarnings("unchecked") 241 | @Override 242 | public T next() { 243 | if(value != null) { 244 | Object result = value; 245 | value = null; 246 | return (T)result; 247 | } 248 | if(arr != null) { 249 | Object result = arr[index]; 250 | index++; 251 | if(index >= arr.length || arr[index] == null) { 252 | arr = null; 253 | index = 0; 254 | } 255 | return (T)result; 256 | } 257 | if(setIter != null) { 258 | Object result = setIter.next(); 259 | if(!setIter.hasNext()) setIter = null; 260 | return (T)result; 261 | } 262 | throw new NoSuchElementException(); 263 | } 264 | 265 | @Override public void remove() {throw new UnsupportedOperationException();} 266 | @Override public Iterator iterator() {return this;} 267 | } 268 | } 269 | -------------------------------------------------------------------------------- /.idea/uiDesigner.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | -------------------------------------------------------------------------------- /core/src_gdx_util/com/matthewmichelotti/collider/RandomXS128.java: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright 2011 See AUTHORS file. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | ******************************************************************************/ 16 | //!!!NOTICE!!! this file has been modified by Matthew Michelotti. 17 | //This file was originally part of the libGDX library 18 | //(https://github.com/libgdx/libgdx) under the Apache License as described 19 | //above. The AUTHORS file specified above can be found at 20 | //https://github.com/libgdx/libgdx/blob/master/AUTHORS . 21 | //The contents of the authors file is as follows: 22 | // 23 | // # This is the official list of the AUTHORS of libgdx 24 | // # for copyright purposes. 25 | // # This file is distinct from the CONTRIBUTORS files. 26 | // # See the latter for an explanation. 27 | // 28 | // # Names should be added to this file as 29 | // # Name or Organization 30 | // # The email address is not required for organizations. 31 | // Mario Zechner 32 | // Nathan Sweet 33 | // 34 | //End of AUTHORS file. 35 | // 36 | //All changes to this file are marked with comments starting with 37 | //my initials (MM). 38 | 39 | //MM: changed package name 40 | //package com.badlogic.gdx.math; 41 | package com.matthewmichelotti.collider; 42 | 43 | import java.util.Random; 44 | 45 | /** This class implements the xorshift128+ algorithm that is a very fast, top-quality 64-bit pseudo-random number generator. The 46 | * quality of this PRNG is much higher than {@link java.util.Random}'s, and its cycle length is 2128 − 1, which 47 | * is more than enough for any single-thread application. More details and algorithms can be found here. 49 | *

50 | * Instances of RandomXS128 are not thread-safe. 51 | * 52 | * @author Inferno 53 | * @author davebaol */ 54 | //MM: changed class from public to package-private 55 | class RandomXS128 extends Random { 56 | 57 | /** Normalization constant for double. */ 58 | private static final double NORM_DOUBLE = 1.0 / (1L << 53); 59 | 60 | /** Normalization constant for float. */ 61 | private static final double NORM_FLOAT = 1.0 / (1L << 24); 62 | 63 | /** The first half of the internal state of this pseudo-random number generator. */ 64 | private long seed0; 65 | 66 | /** The second half of the internal state of this pseudo-random number generator. */ 67 | private long seed1; 68 | 69 | /** Creates a new random number generator. This constructor sets the seed of the random number generator to a value very likely 70 | * to be distinct from any other invocation of this constructor. 71 | *

72 | * This implementation creates a {@link java.util.Random} instance to generate the initial seed. */ 73 | public RandomXS128 () { 74 | setSeed(new Random().nextLong()); 75 | } 76 | 77 | /** Creates a new random number generator using a single {@code long} seed. 78 | * @param seed the initial seed */ 79 | public RandomXS128 (long seed) { 80 | setSeed(seed); 81 | } 82 | 83 | /** Creates a new random number generator using two {@code long} seeds. 84 | * @param seed0 the first part of the initial seed 85 | * @param seed1 the second part of the initial seed */ 86 | public RandomXS128 (long seed0, long seed1) { 87 | setState(seed0, seed1); 88 | } 89 | 90 | /** Returns the next pseudo-random, uniformly distributed {@code long} value from this random number generator's sequence. 91 | *

92 | * Subclasses should override this, as this is used by all other methods. */ 93 | @Override 94 | public long nextLong () { 95 | long s1 = this.seed0; 96 | final long s0 = this.seed1; 97 | this.seed0 = s0; 98 | s1 ^= s1 << 23; 99 | return (this.seed1 = (s1 ^ s0 ^ (s1 >>> 17) ^ (s0 >>> 26))) + s0; 100 | } 101 | 102 | /** This protected method is final because, contrary to the superclass, it's not used anymore by the other methods. */ 103 | @Override 104 | protected final int next (int bits) { 105 | return (int)(nextLong() & ((1L << bits) - 1)); 106 | } 107 | 108 | /** Returns the next pseudo-random, uniformly distributed {@code int} value from this random number generator's sequence. 109 | *

110 | * This implementation uses {@link #nextLong()} internally. */ 111 | @Override 112 | public int nextInt () { 113 | return (int)nextLong(); 114 | } 115 | 116 | /** Returns a pseudo-random, uniformly distributed {@code int} value between 0 (inclusive) and the specified value (exclusive), 117 | * drawn from this random number generator's sequence. 118 | *

119 | * This implementation uses {@link #nextLong()} internally. 120 | * @param n the positive bound on the random number to be returned. 121 | * @return the next pseudo-random {@code int} value between {@code 0} (inclusive) and {@code n} (exclusive). */ 122 | @Override 123 | public int nextInt (final int n) { 124 | return (int)nextLong(n); 125 | } 126 | 127 | /** Returns a pseudo-random, uniformly distributed {@code long} value between 0 (inclusive) and the specified value (exclusive), 128 | * drawn from this random number generator's sequence. The algorithm used to generate the value guarantees that the result is 129 | * uniform, provided that the sequence of 64-bit values produced by this generator is. 130 | *

131 | * This implementation uses {@link #nextLong()} internally. 132 | * @param n the positive bound on the random number to be returned. 133 | * @return the next pseudo-random {@code long} value between {@code 0} (inclusive) and {@code n} (exclusive). */ 134 | public long nextLong (final long n) { 135 | if (n <= 0) throw new IllegalArgumentException("n must be positive"); 136 | for (;;) { 137 | final long bits = nextLong() >>> 1; 138 | final long value = bits % n; 139 | if (bits - value + (n - 1) >= 0) return value; 140 | } 141 | } 142 | 143 | /** Returns a pseudo-random, uniformly distributed {@code double} value between 0.0 and 1.0from this random number generator's 144 | * sequence. 145 | *

146 | * This implementation uses {@link #nextLong()} internally. */ 147 | @Override 148 | public double nextDouble () { 149 | return (nextLong() >>> 11) * NORM_DOUBLE; 150 | } 151 | 152 | /** Returns a pseudo-random, uniformly distributed {@code float} value between 0.0 and 1.0 from this random number generator's 153 | * sequence. 154 | *

155 | * This implementation uses {@link #nextLong()} internally. */ 156 | @Override 157 | public float nextFloat () { 158 | return (float)((nextLong() >>> 40) * NORM_FLOAT); 159 | } 160 | 161 | /** Returns a pseudo-random, uniformly distributed {@code boolean } value from this random number generator's sequence. 162 | *

163 | * This implementation uses {@link #nextLong()} internally. */ 164 | @Override 165 | public boolean nextBoolean () { 166 | return (nextLong() & 1) != 0; 167 | } 168 | 169 | /** Generates random bytes and places them into a user-supplied byte array. The number of random bytes produced is equal to the 170 | * length of the byte array. 171 | *

172 | * This implementation uses {@link #nextLong()} internally. */ 173 | @Override 174 | public void nextBytes (final byte[] bytes) { 175 | int n; // = 0; //MM: commented this assignment 176 | int i = bytes.length; 177 | while (i != 0) { 178 | n = i < 8 ? i : 8; // min(i, 8); 179 | for (long bits = nextLong(); n-- != 0; bits >>= 8) 180 | bytes[--i] = (byte)bits; 181 | } 182 | } 183 | 184 | /** Sets the internal seed of this generator based on the given {@code long} value. 185 | *

186 | * The given seed is passed twice through an hash function. This way, if the user passes a small value we avoid the short 187 | * irregular transient associated with states having a very small number of bits set. 188 | * @param seed a nonzero seed for this generator (if zero, the generator will be seeded with {@link Long#MIN_VALUE}). */ 189 | @Override 190 | public void setSeed (final long seed) { 191 | long seed0 = murmurHash3(seed == 0 ? Long.MIN_VALUE : seed); 192 | setState(seed0, murmurHash3(seed0)); 193 | } 194 | 195 | /** Sets the internal state of this generator. 196 | * @param seed0 the first part of the internal state 197 | * @param seed1 the second part of the internal state */ 198 | public void setState (final long seed0, final long seed1) { 199 | this.seed0 = seed0; 200 | this.seed1 = seed1; 201 | } 202 | 203 | private final static long murmurHash3 (long x) { 204 | x ^= x >>> 33; 205 | x *= 0xff51afd7ed558ccdL; 206 | x ^= x >>> 33; 207 | x *= 0xc4ceb9fe1a85ec53L; 208 | x ^= x >>> 33; 209 | 210 | return x; 211 | } 212 | 213 | } 214 | -------------------------------------------------------------------------------- /core/src/com/matthewmichelotti/collider/HitBox.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2013-2014 Matthew D. Michelotti 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.matthewmichelotti.collider; 18 | 19 | /** 20 | * Description of position, shape, and velocities of a hitbox 21 | * used for testing collision with other HitBoxes. 22 | * Methods for creating HitBoxes can be found in the {@link Collider} class. 23 | * Based on the velocities, the position and shape of a HitBox will 24 | * automatically reflect changes in time in the Collider. 25 | * The word "hitbox" is used in a general sense, as a HitBox is 26 | * not necessarily a rectangle. 27 | *

28 | * When a HitBox's state is modified, it will need to check with the Collider 29 | * for potential interactions. 30 | * Will wait for all consecutive modifier method calls on a HitBox 31 | * before performing this check. 32 | * Whenever you modify the state of a HitBox, you must also call 33 | * {@link #commit(double)}. 34 | *

35 | * For the sake of avoiding numerical instability, the dimensions 36 | * of a HitBox should never be zero nor extremely small. 37 | * Something on the order of 1/10 of a "pixel" should still be fine, 38 | * but, for example, 1e-11 "pixels" is too small. 39 | * 40 | * @author Matthew Michelotti 41 | */ 42 | public abstract class HitBox { 43 | /** 44 | * Number of legal HitBox groups. 45 | * Current value is 256, but it most cases you won't need more than 3 groups. 46 | * Legal groups are 0-255 inclusive. 47 | */ 48 | public final static int NUM_GROUPS = 256; 49 | 50 | double startTime, endTime; 51 | final Collider collider; 52 | Object overlapSet; 53 | 54 | private int group = -2; 55 | private int changeId = 0; 56 | private int testId = -1; 57 | private Object owner; 58 | 59 | HitBox(Collider collider) { 60 | this.collider = collider; 61 | } 62 | 63 | void init() { 64 | this.startTime = collider.getTime(); 65 | this.endTime = this.startTime; 66 | 67 | this.group = -1; 68 | setGroup(0); 69 | } 70 | 71 | void markTransitionStart() { 72 | startTime = collider.getTime(); 73 | if(endTime < startTime) { 74 | throw new RuntimeException("updating HitBox late"); 75 | } 76 | changeId++; 77 | endTime = -1; 78 | } 79 | 80 | /** 81 | * Call when done using this HitBox. 82 | * No more events will be generated involving this HitBox. 83 | * This object will be placed uninitialized in a pool 84 | * for future use. 85 | */ 86 | public void free() { 87 | //NOTE: overridden free method should place the HitBox in the appropriate pool 88 | owner = null; 89 | group = -2; 90 | } 91 | 92 | boolean isInitialized() { 93 | return group != -2; 94 | } 95 | 96 | final boolean testMark(int testId) { 97 | if(testId == this.testId) return false; 98 | this.testId = testId; 99 | return true; 100 | } 101 | 102 | final int getChangeId() {return changeId;} 103 | 104 | /** 105 | * Call this method if there is a change in the return values 106 | * of {@link InteractTester#canInteract(HitBox, HitBox)} involving 107 | * this HitBox. 108 | * This will prompt searching for potential collisions between 109 | * previously uninteractable HitBoxes. 110 | * Separate events will not be generated for HitBoxes that overlap and used 111 | * to interact with each other but no longer do because of this call. 112 | */ 113 | public final void interactivityChange() {collider.altering(this, true);} 114 | 115 | /** 116 | * Set the group that this HitBox belongs to. 117 | * Default group is 0. 118 | * The value -1 denotes that this HitBox does not belong to any group 119 | * and thus is never tested for collisions. 120 | * This method will also invoke the functionality of 121 | * {@link #interactivityChange()}. 122 | *

123 | * Collision testing will only be performed on HitBoxes of the groups 124 | * specified by the {@link InteractTester#getInteractGroups(HitBox)} 125 | * method. 126 | * This reduces the number of HitBoxes to iterate over for collision checks. 127 | * It is a good idea to use only a small number of groups for a game, perhaps 1 to 3. 128 | * As an example, if you are implementing a 129 | * danmaku 130 | * game, you might use one group for bullets and one group for everything else, 131 | * and make it so bullets do not check for collisions within the bullet group. 132 | * @param group Group that this HitBox should belong to. Must be between 133 | * -1 and {@link #NUM_GROUPS}-1 inclusive. The value -1 denotes not belonging to any group. 134 | */ 135 | public final void setGroup(int group) { 136 | if(group < -1 || group >= NUM_GROUPS) { 137 | throw new IllegalArgumentException("invalid group:" + group); 138 | } 139 | collider.altering(this, true); 140 | this.group = group; 141 | } 142 | 143 | /** 144 | * This should be called after you modify the HitBox by changing 145 | * its position, velocity, interactivity, etc. (exception: this does 146 | * not need to be called after calling {@link #setOwner(Object)}). 147 | * Call this method only once after you have made all of the other 148 | * changes to this HitBox. You must specify an endTime, which 149 | * is the expected time of the next change to the HitBox state. 150 | * You must call commit again when this endTime is reached, if not sooner. 151 | * Although you are allowed to change the HitBox state and call commit prior 152 | * to the specified endTime, doing so will result in more collisions 153 | * that need to be tested. 154 | * @param endTime Expected time of next change to HitBox state. 155 | * Positive infinity is allowed. 156 | */ 157 | public final void commit(double endTime) { 158 | double time = collider.getTime(); 159 | if(endTime < time) throw new IllegalArgumentException("endTime already passed"); 160 | collider.altering(this); 161 | this.endTime = endTime; 162 | } 163 | 164 | /** 165 | * Set an object to be associated with this HitBox. 166 | * This is provided as an alternative to looking up 167 | * a related object in a HashMap with HitBoxes as keys. 168 | * @param obj Object to be associated with this HitBox. 169 | */ 170 | public final void setOwner(Object obj) {this.owner = obj;} 171 | 172 | /** 173 | * Returns the group that this HitBox belongs to. 174 | * @return The group that this HitBox belongs to. 175 | * @see #setGroup(int) 176 | */ 177 | public final int getGroup() {return group;} 178 | 179 | /** 180 | * Returns the Object associated with this HitBox. 181 | * @return The Object associated with this HitBox. 182 | */ 183 | public final Object getOwner() {return owner;} 184 | 185 | /** 186 | * Returns the current time of the simulation. 187 | * Same as calling {@link Collider#getTime()}. 188 | * @return The current time of the simulation. 189 | */ 190 | public final double getTime() {return collider.getTime();} 191 | 192 | /** 193 | * Returns a normal vector between the two HitBoxes. 194 | * Vector will be pointed away from this HitBox and towards the dest HitBox. 195 | * @param dest Other HitBox that normal vector will be pointed towards. 196 | * @return Normal vector between the two HitBoxes. 197 | * This object will be re-used each time getNormal is called 198 | * on any HitBox generated from the same Collider. 199 | */ 200 | public final Normal getNormal(HitBox dest) { 201 | return collider.getNormal(this, dest); 202 | } 203 | 204 | /** 205 | * Returns the amount that the two HitBoxes overlap. 206 | * This is the same as the overlap in the return 207 | * value of {@link #getNormal(HitBox)}. 208 | * Due to rounding error, this value may be positive 209 | * even if a collision event was not generated 210 | * for the two HitBoxes, and vice versa. 211 | * @param other Other HitBox to compute overlap of. 212 | * @return The amount that the two HitBoxes overlap. 213 | */ 214 | public final double getOverlap(HitBox other) { 215 | return collider.getNormal(this, other).overlap; 216 | } 217 | 218 | 219 | /** 220 | * Returns true if the two HitBoxes overlap. 221 | * This is the same as checking if {@link #getOverlap(HitBox)} 222 | * is positive. 223 | * Due to rounding error, this value may be true 224 | * even if a collision event was not generated 225 | * for the two HitBoxes, and vice versa. 226 | * @param other Other HitBox to test overlap with. 227 | * @return True if the two HitBoxes overlap. 228 | */ 229 | public final boolean overlaps(HitBox other) { 230 | return collider.getNormal(this, other).overlap > 0.0; 231 | } 232 | 233 | abstract boolean isMoving(); 234 | 235 | abstract double getBoundEdgeComp(int edge, double startTime, double endTime); 236 | 237 | abstract double getMaxBoundEdgeVel(); 238 | 239 | final double getBoundEdgeComp(int edge) { 240 | return getBoundEdgeComp(edge, startTime, endTime); 241 | } 242 | } 243 | -------------------------------------------------------------------------------- /demos-core/src/com/matthewmichelotti/collider/demos/GameEngine.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2013-2014 Matthew D. Michelotti 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.matthewmichelotti.collider.demos; 18 | 19 | import java.util.ArrayList; 20 | import java.util.Collections; 21 | import java.util.HashSet; 22 | import java.util.PriorityQueue; 23 | 24 | import com.badlogic.gdx.Gdx; 25 | import com.badlogic.gdx.graphics.Color; 26 | import com.badlogic.gdx.graphics.GL20; 27 | import com.badlogic.gdx.graphics.OrthographicCamera; 28 | import com.badlogic.gdx.graphics.g2d.BitmapFont; 29 | import com.badlogic.gdx.graphics.g2d.SpriteBatch; 30 | import com.badlogic.gdx.graphics.glutils.ShapeRenderer; 31 | import com.badlogic.gdx.graphics.glutils.ShapeRenderer.ShapeType; 32 | import com.badlogic.gdx.utils.TimeUtils; 33 | import com.matthewmichelotti.collider.Collider; 34 | import com.matthewmichelotti.collider.ColliderEvent; 35 | import com.matthewmichelotti.collider.ColliderOpts; 36 | import com.matthewmichelotti.collider.HBCircle; 37 | import com.matthewmichelotti.collider.HBRect; 38 | import com.matthewmichelotti.collider.HitBox; 39 | import com.matthewmichelotti.collider.InteractTester; 40 | import com.matthewmichelotti.collider.demos.comps.CBounds; 41 | import com.matthewmichelotti.collider.util.ColliderListener; 42 | import com.matthewmichelotti.collider.util.ColliderProcess; 43 | import com.matthewmichelotti.collider.util.ContProcesses; 44 | import com.matthewmichelotti.collider.util.ContProcess; 45 | 46 | /** 47 | * Manages FunctionEvents, ColliderEvents, 48 | * creation of new HitBoxes, and rendering. 49 | * @author Matthew Michelotti 50 | */ 51 | public class GameEngine { 52 | public final static int SCREEN_WIDTH = 1280; //960 53 | public final static int SCREEN_HEIGHT = 720; 54 | 55 | public final static int GROUP_NORMAL = 0; 56 | public final static int GROUP_BULLET = 1; 57 | 58 | private final static int[] ALL_GROUPS_ARR = new int[] {GROUP_NORMAL, GROUP_BULLET}; 59 | private final static int[] NORMAL_GROUP_ARR = new int[] {GROUP_NORMAL}; 60 | 61 | private ContProcesses processes; 62 | private Collider collider; 63 | private HashSet comps = new HashSet(); 64 | private PriorityQueue events = new PriorityQueue(); 65 | private MousePosListener mouseListener; 66 | private CBounds bounds; 67 | 68 | private ShapeRenderer shapeR = new ShapeRenderer(); 69 | private SpriteBatch spriteB = new SpriteBatch(); 70 | private BitmapFont font = new BitmapFont(); 71 | private Color bgColor = Color.BLACK; 72 | private OrthographicCamera camera; 73 | 74 | private long renderWork = 0; 75 | private long otherWork = 0; 76 | 77 | public GameEngine() { 78 | clear(); 79 | camera = new OrthographicCamera(SCREEN_WIDTH, SCREEN_HEIGHT); 80 | camera.translate(.5f*SCREEN_WIDTH, .5f*SCREEN_HEIGHT, 0); 81 | camera.update(); 82 | } 83 | 84 | public void clear() { 85 | processes = new ContProcesses(); 86 | 87 | ColliderOpts opts = new ColliderOpts(); 88 | opts.cellWidth = 22.0; 89 | opts.separateBuffer = .1; 90 | opts.maxForesightTime = 2.0; 91 | opts.interactTester = new CompInteractTester(); 92 | collider = new Collider(opts); 93 | 94 | comps.clear(); 95 | events.clear(); 96 | mouseListener = null; 97 | bounds = null; 98 | 99 | bgColor = Color.BLACK; 100 | 101 | processes.addProcess(new ColliderProcess(collider, new MyColliderListener())); 102 | processes.addProcess(new EventProcess()); 103 | events.add(new LogEvent(0.0)); 104 | } 105 | 106 | public HBRect makeRect() {return collider.makeRect();} 107 | public HBCircle makeCircle() {return collider.makeCircle();} 108 | 109 | public void addEvent(FunctionEvent event) { 110 | events.add(event); 111 | } 112 | 113 | public void addComp(Component comp) { 114 | boolean success = comps.add(comp); 115 | if(!success) throw new RuntimeException(); 116 | if(comp instanceof MousePosListener) { 117 | if(mouseListener != null) throw new RuntimeException(); 118 | mouseListener = (MousePosListener)comp; 119 | } 120 | if(comp instanceof CBounds) { 121 | if(bounds != null) throw new RuntimeException(); 122 | bounds = (CBounds)comp; 123 | } 124 | } 125 | 126 | public void removeComp(Component comp) { 127 | boolean success = comps.remove(comp); 128 | if(!success) throw new RuntimeException(); 129 | if(mouseListener == comp) mouseListener = null; 130 | if(bounds == comp) bounds = null; 131 | } 132 | 133 | public boolean isInBounds(HitBox hitBox) { 134 | return hitBox.getOverlap(bounds.hitBox()) >= .1; 135 | } 136 | 137 | public void stepToTime(double time) { 138 | if(time < getTime()) return; 139 | long startNanoTime = TimeUtils.nanoTime(); 140 | if(time > getTime() && mouseListener != null) { 141 | double x = SCREEN_WIDTH*Gdx.input.getX()/(double)Gdx.graphics.getWidth(); 142 | double y = SCREEN_HEIGHT*(1.0 - Gdx.input.getY()/(double)Gdx.graphics.getHeight()); 143 | x = Math.max(0, Math.min(SCREEN_WIDTH, x)); 144 | y = Math.max(0, Math.min(SCREEN_HEIGHT, y)); 145 | mouseListener.updateMousePos(time, x, y); 146 | } 147 | processes.stepToTime(time); 148 | otherWork += (TimeUtils.nanoTime() - startNanoTime); 149 | } 150 | 151 | public double getTime() { 152 | return processes.getTime(); 153 | } 154 | 155 | public void setBG(Color color) { 156 | this.bgColor = color; 157 | } 158 | 159 | public void render(boolean drawFPS) { 160 | long startNanoTime = TimeUtils.nanoTime(); 161 | 162 | spriteB.setProjectionMatrix(camera.combined); 163 | shapeR.setProjectionMatrix(camera.combined); 164 | 165 | Gdx.gl.glClearColor(bgColor.r, bgColor.g, bgColor.b, bgColor.a); 166 | Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT); 167 | 168 | ArrayList orderedComps = new ArrayList(comps); 169 | ArrayList messageComps = new ArrayList(); 170 | Collections.sort(orderedComps); 171 | 172 | shapeR.begin(ShapeType.Filled); 173 | for(Component c : orderedComps) { 174 | if(c.getMessage() != null) messageComps.add(c); 175 | Color color = c.getColor(); 176 | if(color == null) continue; 177 | shapeR.setColor(color); 178 | if(c.isRect()) { 179 | HBRect rect = c.rect(); 180 | double l = rect.getX() - .5*rect.getWidth(); 181 | double b = rect.getY() - .5*rect.getHeight(); 182 | shapeR.rect((float)l, (float)b, (float)rect.getWidth(), (float)rect.getHeight()); 183 | } 184 | else { 185 | HBCircle circle = c.circ(); 186 | shapeR.circle((float)circle.getX(), (float)circle.getY(), .5f*(float)circle.getDiam()); 187 | } 188 | } 189 | shapeR.end(); 190 | 191 | spriteB.begin(); 192 | 193 | font.setColor(Color.WHITE); 194 | for(Component c : messageComps) { 195 | String message = c.getMessage(); 196 | BitmapFont.TextBounds tb = font.getBounds(message); 197 | float x = (float)c.hitBox().getX() - .5f*tb.width; 198 | float y = (float)c.hitBox().getY() + .5f*tb.height; 199 | font.draw(spriteB, message, x, y); 200 | } 201 | 202 | if(drawFPS) { 203 | font.draw(spriteB, "FPS: "+ Gdx.graphics.getFramesPerSecond(), 0, SCREEN_HEIGHT); 204 | } 205 | 206 | spriteB.end(); 207 | 208 | renderWork += (TimeUtils.nanoTime() - startNanoTime); 209 | } 210 | 211 | public void dispose() { 212 | shapeR.dispose(); 213 | spriteB.dispose(); 214 | font.dispose(); 215 | } 216 | 217 | private class LogEvent extends FunctionEvent { 218 | private LogEvent(double time) {super(time);} 219 | 220 | @Override public void resolve() { 221 | collider.log(); 222 | System.out.println("Free Memory: " + Runtime.getRuntime().freeMemory()); 223 | System.out.println("Total Memory: " + Runtime.getRuntime().totalMemory()); 224 | System.out.println("Rendering Work: " + renderWork*1e-9); 225 | System.out.println("Other Work: " + otherWork*1e-9); 226 | System.out.println("FPS: " + Gdx.graphics.getFramesPerSecond()); 227 | renderWork = 0; 228 | otherWork = 0; 229 | setTime(getTime() + 3.0); 230 | addEvent(this); 231 | } 232 | } 233 | 234 | private static class MyColliderListener implements ColliderListener { 235 | @Override public void collision(ColliderEvent evt) { 236 | Component compA = (Component)evt.getFirst().getOwner(); 237 | Component compB = (Component)evt.getSecond().getOwner(); 238 | compA.onCollide(compB); 239 | if(!compA.isDeleted() && !compB.isDeleted()) compB.onCollide(compA); 240 | } 241 | @Override public void separation(ColliderEvent evt) { 242 | Component compA = (Component)evt.getFirst().getOwner(); 243 | Component compB = (Component)evt.getSecond().getOwner(); 244 | compA.onSeparate(compB); 245 | if(!compA.isDeleted() && !compB.isDeleted()) compB.onSeparate(compA); 246 | } 247 | } 248 | 249 | private class EventProcess implements ContProcess { 250 | @Override public double peekNextEventTime() { 251 | FunctionEvent event = events.peek(); 252 | if(event == null) return Double.POSITIVE_INFINITY; 253 | return event.getTime(); 254 | } 255 | @Override public void stepToTime(double time) {} 256 | @Override public void resolveEvent() { 257 | FunctionEvent event = events.poll(); 258 | if(event.getTime() == processes.getTime()) event.resolve(); 259 | } 260 | } 261 | 262 | private static class CompInteractTester implements InteractTester { 263 | @Override public boolean canInteract(HitBox a, HitBox b) { 264 | Component compA = (Component)a.getOwner(); 265 | Component compB = (Component)b.getOwner(); 266 | return compA.canInteract(compB) || compB.canInteract(compA); 267 | } 268 | 269 | @Override public int[] getInteractGroups(HitBox hitBox) { 270 | if(((Component)hitBox.getOwner()).interactsWithBullets()) return ALL_GROUPS_ARR; 271 | return NORMAL_GROUP_ARR; 272 | } 273 | } 274 | } 275 | -------------------------------------------------------------------------------- /core/src/com/matthewmichelotti/collider/CollisionTester.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2013-2014 Matthew D. Michelotti 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.matthewmichelotti.collider; 18 | 19 | final class CollisionTester { 20 | private double separateBuffer; 21 | private HBRect dummyRect = new HBRect(null); 22 | private HBCircle dummyPoint = new HBCircle(null); 23 | private HBRect dummyRect2 = new HBRect(null); 24 | private HBCircle dummyCircle = new HBCircle(null); 25 | private Normal normal = new Normal(); 26 | 27 | CollisionTester(ColliderOpts opts) { 28 | this.separateBuffer = opts.separateBuffer; 29 | if(this.separateBuffer <= 0.0) throw new IllegalArgumentException(); 30 | } 31 | 32 | double collideTime(HitBox a, HitBox b, double startTime) { 33 | double endTime = Arith.min(a.endTime, b.endTime); 34 | if(endTime <= startTime) return Double.POSITIVE_INFINITY; 35 | if(!boundBoxTest(a, b, startTime, endTime)) return Double.POSITIVE_INFINITY; 36 | return getTime(a, b, startTime, endTime, true); 37 | } 38 | 39 | double separateTime(HitBox a, HitBox b, double startTime) { 40 | double endTime = Arith.min(a.endTime, b.endTime); 41 | if(endTime <= startTime) return Double.POSITIVE_INFINITY; 42 | boolean aIsRect = (a.getClass() == HBRect.class); 43 | boolean bIsRect = (b.getClass() == HBRect.class); 44 | if(aIsRect && bIsRect) { 45 | HBRect rect = (HBRect)a; 46 | double hw = rect.startHW; 47 | double hh = rect.startHH; 48 | rect.startHW += separateBuffer; 49 | rect.startHH += separateBuffer; 50 | double result = getTime(a, b, startTime, endTime, false); 51 | rect.startHW = hw; 52 | rect.startHH = hh; 53 | return result; 54 | } 55 | else { 56 | HBCircle circ = (HBCircle)(aIsRect ? b : a); 57 | double rad = circ.startRad; 58 | circ.startRad += separateBuffer; 59 | double result = getTime(a, b, startTime, endTime, false); 60 | circ.startRad = rad; 61 | return result; 62 | } 63 | } 64 | 65 | Normal normal(HitBox src, HitBox dst, double time) { 66 | if(src.getClass() == HBRect.class) { 67 | if(dst.getClass() == HBRect.class) return rectRectNormal((HBRect)src, (HBRect)dst, time); 68 | else return rectCircNormal((HBRect)src, (HBCircle)dst, time); 69 | } 70 | else { 71 | if(dst.getClass() == HBRect.class) { 72 | Normal normal = rectCircNormal((HBRect)dst, (HBCircle)src, time); 73 | normal.x = -normal.x; 74 | normal.y = -normal.y; 75 | return normal; 76 | } 77 | else return circCircNormal((HBCircle)src, (HBCircle)dst, time); 78 | } 79 | } 80 | 81 | private double getTime(HitBox a, HitBox b, double startTime, double endTime, boolean forCollide) { 82 | boolean aIsRect = (a.getClass() == HBRect.class); 83 | boolean bIsRect = (b.getClass() == HBRect.class); 84 | double result; 85 | if(aIsRect) { 86 | if(bIsRect) result = rectRectTime((HBRect)a, (HBRect)b, startTime, endTime, forCollide); 87 | else result = rectCircTime((HBRect)a, (HBCircle)b, startTime, endTime, forCollide); 88 | } 89 | else { 90 | if(bIsRect) result = rectCircTime((HBRect)b, (HBCircle)a, startTime, endTime, forCollide); 91 | else result = circCircTime((HBCircle)a, (HBCircle)b, startTime, forCollide); 92 | } 93 | if(result >= endTime) result = Double.POSITIVE_INFINITY; 94 | return result; 95 | } 96 | 97 | private double rectCircTime(HBRect a, HBCircle b, double startTime, double endTime, 98 | boolean forCollide) 99 | { 100 | if(forCollide) return rectCircCollideTime(a, b, startTime, endTime); 101 | else return rectCircSeparateTime(a, b, startTime, endTime); 102 | } 103 | 104 | private static boolean boundBoxTest(HitBox a, HitBox b, double startTime, double endTime) { 105 | for(int dir = 0; dir < 4; dir++) { 106 | double overlap = a.getBoundEdgeComp(dir, startTime, endTime) 107 | + b.getBoundEdgeComp(Dir.opp(dir), startTime, endTime); 108 | if(overlap <= 0.0) return false; 109 | } 110 | return true; 111 | } 112 | 113 | private static double rectRectTime(HBRect a, HBRect b, double startTime, double endTime, 114 | boolean forCollide) 115 | { 116 | double overlapStart = 0.0; 117 | double overlapEnd = 1.05*(endTime - startTime); 118 | 119 | for(int dir = 0; dir < 4; dir++) { 120 | double overlap = a.getEdgeComp(dir, startTime) + b.getEdgeComp(Dir.opp(dir), startTime); 121 | double overlapVel = a.getVelEdgeComp(dir) + b.getVelEdgeComp(Dir.opp(dir)); 122 | if(overlap < 0.0) { 123 | if(!forCollide) return startTime; 124 | if(overlapVel <= 0.0) return Double.POSITIVE_INFINITY; 125 | else overlapStart = Arith.max(overlapStart, -overlap/overlapVel); 126 | } 127 | else if(overlapVel < 0.0) { 128 | overlapEnd = Arith.min(overlapEnd, -overlap/overlapVel); 129 | } 130 | if(overlapStart >= overlapEnd) return forCollide ? Double.POSITIVE_INFINITY : startTime; 131 | } 132 | 133 | return startTime + (forCollide ? overlapStart : overlapEnd); 134 | } 135 | 136 | private static double circCircTime(HBCircle a, HBCircle b, double startTime, boolean forCollide) 137 | { 138 | double sign = forCollide ? 1.0 : -1.0; 139 | 140 | double netRad = a.getRad(startTime) + b.getRad(startTime); 141 | double distX = a.getX(startTime) - b.getX(startTime); 142 | double distY = a.getY(startTime) - b.getY(startTime); 143 | 144 | double coeffC = sign*(netRad*netRad - distX*distX - distY*distY); 145 | if(coeffC > 0.0) return startTime; 146 | 147 | double netRadVel = a.velRad + b.velRad; 148 | double distXVel = a.velX - b.velX; 149 | double distYVel = a.velY - b.velY; 150 | 151 | double coeffA = sign*(netRadVel*netRadVel - distXVel*distXVel - distYVel*distYVel); 152 | double coeffB = sign*2.0*(netRad*netRadVel - distX*distXVel - distY*distYVel); 153 | 154 | double result = Arith.quadRootAscending(coeffA, coeffB, coeffC); 155 | if(result >= 0.0) return startTime + result; 156 | else return Double.POSITIVE_INFINITY; //NOTE: handles NaN case 157 | } 158 | 159 | private double rectCircCollideTime(HBRect a, HBCircle b, double startTime, double endTime) 160 | { 161 | // if(!forCollide) { 162 | // pair.init(a, b); 163 | // double overlap = pair.getOverlap(); 164 | // pair.clear(); 165 | // if(overlap <= 0.0) return startTime; 166 | // } 167 | dummyRect.dummyMimicCircle(b); 168 | double time = rectRectTime(a, dummyRect, startTime, endTime, true); 169 | if(time >= endTime) return Double.POSITIVE_INFINITY; 170 | // if(time >= endTime) { 171 | // if(forCollide) return Double.POSITIVE_INFINITY; 172 | // else time = endTime; 173 | // } 174 | 175 | for(int dir = 0; dir < 2; dir++) { 176 | double hiEdge = a.getEdgeComp(dir, time); 177 | double loEdge = -a.getEdgeComp(Dir.opp(dir), time); 178 | double bCoord = b.getPosComp(dir, time); 179 | if(bCoord > hiEdge) { 180 | dummyPoint.dummySetStartCoord(dir, hiEdge); 181 | dummyPoint.dummySetVelCoord(dir, a.getVelEdgeComp(dir)); 182 | } 183 | else if(bCoord < loEdge) { 184 | dummyPoint.dummySetStartCoord(dir, loEdge); 185 | dummyPoint.dummySetVelCoord(dir, -a.getVelEdgeComp(Dir.opp(dir))); 186 | } 187 | else return time; 188 | } 189 | dummyPoint.startTime = time; 190 | return circCircTime(dummyPoint, b, startTime, true); 191 | } 192 | 193 | private double rectCircSeparateTime(HBRect a, HBCircle b, double startTime, double endTime) { 194 | Normal normal = normal(a, b, startTime); 195 | if(normal.overlap <= 0.0) return startTime; 196 | mirror(a, dummyRect2, endTime); 197 | mirror(b, dummyCircle, endTime); 198 | dummyRect2.startTime = 0.0; 199 | dummyCircle.startTime = 0.0; 200 | double result = rectCircCollideTime(dummyRect2, dummyCircle, 0.0, endTime - startTime); 201 | return Arith.max(startTime, endTime - result); 202 | } 203 | 204 | private static void mirror(HBRect original, HBRect mirror, double endTime) { 205 | mirrorPos(original, mirror, endTime); 206 | mirror.startHW = original.getHW(endTime); 207 | mirror.startHH = original.getHH(endTime); 208 | mirror.velHW = -original.velHW; 209 | mirror.velHH = -original.velHH; 210 | } 211 | 212 | private static void mirror(HBCircle original, HBCircle mirror, double endTime) { 213 | mirrorPos(original, mirror, endTime); 214 | mirror.startRad = original.getRad(endTime); 215 | mirror.velRad = -original.velRad; 216 | } 217 | 218 | private static void mirrorPos(HBPositioned original, HBPositioned mirror, double endTime) { 219 | mirror.startX = original.getX(endTime); 220 | mirror.startY = original.getY(endTime); 221 | mirror.velX = -original.velX; 222 | mirror.velY = -original.velY; 223 | } 224 | 225 | private Normal rectRectNormal(HBRect src, HBRect dst, double time) { 226 | int minDir = 0; 227 | double overlap = Double.POSITIVE_INFINITY; 228 | for(int dir = 0; dir < 4; dir++) { 229 | double testOverlap = src.getEdgeComp(dir, time) 230 | + dst.getEdgeComp(Dir.opp(dir), time); 231 | if(testOverlap < overlap) { 232 | overlap = testOverlap; 233 | minDir = dir; 234 | } 235 | } 236 | normal.x = Dir.x(minDir); 237 | normal.y = Dir.y(minDir); 238 | normal.overlap = overlap; 239 | return normal; 240 | } 241 | 242 | private Normal circCircNormal(HBCircle src, HBCircle dst, double time) { 243 | double nx = dst.getX(time) - src.getX(time); 244 | double ny = dst.getY(time) - src.getY(time); 245 | double dist = Math.sqrt(nx*nx + ny*ny); 246 | if(dist == 0.0) { 247 | nx = 1.0; 248 | ny = 0.0; 249 | } 250 | else {//NOTE: if dist != 0.0, dist is at least Math.sqrt(Double.MIN_VALUE) 251 | double invNMag = 1.0/dist; 252 | nx *= invNMag; 253 | ny *= invNMag; 254 | } 255 | normal.x = nx; 256 | normal.y = ny; 257 | normal.overlap = src.getRad(time) + dst.getRad(time) - dist; 258 | return normal; 259 | } 260 | 261 | private Normal rectCircNormal(HBRect src, HBCircle dst, double time) { 262 | for(int dir = 0; dir < 2; dir++) { 263 | double dstCoord = dst.getPosComp(dir, time); 264 | double srcHi = src.getEdgeComp(dir, time); 265 | double srcLo = -src.getEdgeComp(Dir.opp(dir), time); 266 | if(dstCoord > srcHi) dummyPoint.dummySetStartCoord(dir, srcHi); 267 | else if(dstCoord < srcLo) dummyPoint.dummySetStartCoord(dir, srcLo); 268 | else { 269 | dummyRect.dummyMimicCircle(dst); 270 | return rectRectNormal(src, dummyRect, time); 271 | } 272 | } 273 | dummyPoint.velX = 0.0; 274 | dummyPoint.velY = 0.0; 275 | return circCircNormal(dummyPoint, dst, time); 276 | } 277 | } 278 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "{}" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright {yyyy} {name of copyright owner} 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | 203 | --------------------------------------------------------------------------------