mattbrand Posted November 10, 2016 Share Posted November 10, 2016 I'm using P2 physics, and I'm creating a relatively intricate border around the screen that I want balls to bounce against. I am scaling all assets on the screen for 3 different aspect ratios, depending on the user's screen size. I create the border like this: bgOverlay = otherSpriteGroup.create(gameWidth / 2, gameHeight / 2, "bgOverlay"); bgOverlay.anchor.setTo(0.5, 0.5); bgOverlay.scale.setTo(scale.x, scale.y); bgOverlay.body.clearShapes(); bgOverlay.body.loadPolygon("physicsData", "bgOverlay"); bgOverlay.body.setCollisionGroup(otherCollisionGroup); bgOverlay.body.collides(bubbleCollisionGroup, otherHitBubble, this); bgOverlay.body.static = true; The trick is that I scale all assets using a Phaser Point, scale.x and scale.y. But when I load in the polygon data using bgOverlay.body.loadPolygon, it takes in actual pixel values, and I can't scale bgOverlay.body. I am not sure how to scale the numbers in the physics data. Any ideas? Link to comment Share on other sites More sharing options...
mattbrand Posted November 10, 2016 Author Share Posted November 10, 2016 One way I thought I could handle this was to have 3 different sets of polygon data, one for each resolution. But I'm still wondering if there is a way to scale a physics body. Link to comment Share on other sites More sharing options...
Tom Atom Posted November 12, 2016 Share Posted November 12, 2016 Hi, I have solution, that is pretty complex - once you created body with collider, you cannot scale it, but you can scale it while crating it. My solution is in TypeScript. It can handle objects with compound colliders too - for eaxample: one sprite with two circle colliders and one polygon collider. Objects in game I was working on were obstacles, so this is reason, why I use Obstacle in code below. First I define a few interfaces: export interface IPoint { x: number; y: number; } export interface ICircle { radius: number; x: number; y: number; } export interface IRect { width: number; height: number; x: number; y: number; } export interface IPoly { points: number[]; } export interface ICollision { type: string; data: ICircle | IRect | IPoly; } export interface IObstacle { collisions: ICollision[]; touchPoint: IPoint; scale?: number; } Simply: Obstacle is defined with array of colliders (ICollision) and optional scale if different from 1. You can ingnore touchPoint - it was game specific. Then I have class P2Sprite extending Phaser.Sprite: export class P2Sprite extends Phaser.Sprite { protected _savePosition = new Phaser.Point(0, 0); protected _saveScale: number = 1; // ------------------------------------------------------------------------- public setColliders(collisions: ICollision[], scale:number, flipped: boolean): void { this._saveScale = scale; let body = <Phaser.Physics.P2.Body>this.body; let axOffset = (0.5 - this.anchor.x) * this.width * (flipped ? -1 : 1); let ayOffset = (0.5 - this.anchor.y) * this.height; body.clearShapes(); for (let i = 0; i < collisions.length; i++) { let c = collisions[i]; switch (c.type) { case "circle": { let obj = <ICircle>c.data; body.addCircle( obj.radius * scale, (flipped ? this.width - obj.x : obj.x) * scale - this.width / 2 + axOffset, obj.y * scale - this.height / 2 + ayOffset); } break; case "rect": { let obj = <IRect>c.data; body.addRectangle( obj.width * scale, obj.height * scale, (flipped ? this.width - obj.x - obj.width / 2 : obj.x + obj.width / 2) * scale - this.width / 2 + axOffset, (obj.y + obj.height / 2) * scale - this.height / 2 + ayOffset); } break; case "poly": { let defPts = (<IPoly>c.data).points; let points: number[] = []; if (!flipped) { for (let i = 0; i < defPts.length; i += 2) { points[i] = defPts[i] * scale + axOffset; points[i + 1] = defPts[i + 1] * scale + ayOffset; } } else { for (let i = defPts.length - 2, j = 0; i >= 0; i -= 2, j += 2) { points[j] = (this.width - defPts[i]) * scale + axOffset; points[j + 1] = defPts[i + 1] * scale + ayOffset; } } let polyData = [{ "shape": points }]; body.loadPolygon(null, polyData); } break; default: console.error("unknown type of collision " + c.type); break; } } } // ------------------------------------------------------------------------- public setPosition(x: number, y: number): void { let body = <Phaser.Physics.P2.Body>this.body; body.x = x; body.y = y; this._savePosition.set(x, y); } // ------------------------------------------------------------------------- public setRotation(angle: number): void { let body = <Phaser.Physics.P2.Body>this.body; body.angle = angle; } // ------------------------------------------------------------------------- public setCollisionGroup(group: Phaser.Physics.P2.CollisionGroup): void { (<Phaser.Physics.P2.Body>this.body).setCollisionGroup(group); } // ------------------------------------------------------------------------- public collides(group: Phaser.Physics.P2.CollisionGroup | Phaser.Physics.P2.CollisionGroup[], callback?: Function, callbackContext?: any): void { (<Phaser.Physics.P2.Body>this.body).collides(group, callback, callbackContext); } } In setColliders is core of what you are looking for - it takes array of IColliders and adjusts it to scale and horizontal flip. Vertical flip was not needed, as horizontal flip with 180 degrees rotation can simulate it. Now, how to use it. Obstacles are defined like this: const OBSTACLE_DEFS: { [id: string]: IObstacle; } = { Letajici0: { collisions: [ { type: "circle", data: { radius: 35, x: 255, y: 102 } }, { type: "circle", data: { radius: 35, x: 253, y: 159 } }, { type: "poly", data: { points: [245,66, /**/ 245, 191, /**/ 203, 173, /**/ 150, 128, /**/ 203, 83] } }, { type: "rect", data: { width: 131, height: 8, x: 23, y: 125 } } ], touchPoint: { x: 130, y: 0 } }, Bonus2: { collisions: [ { type: "circle", data: { radius: 27, x: 47, y: 91 } }, { type: "circle", data: { radius: 27, x: 87, y: 51 } } ], touchPoint: { x: 0, y: 0 }, scale: 0.75 } }; Here are two obstacles with names Letajici0 and Bonus2. First one has compound collider from 4 coliders - 2x circle, 1x polygon. 1x rectangle. Again, ignore touchPoint. Second has 2 circles and should be scaled to 0.75. Obstacle class extends P2Sprite and can process these data: export class Obstacle extends P2Sprite { private _touchPoint = new Phaser.Point(); private _tag: eObstacleTag; // ------------------------------------------------------------------------- public constructor(game: Phaser.Game, name: string, flipped: boolean, tag: eObstacleTag) { super(game, 0, 0, "Sprites", name); this._tag = tag; // create sprite let frame = game.cache.getFrameByName("Sprites", name); // enable the physics body on this sprite and turn on the visual debugger let p2 = <Phaser.Physics.P2>game.physics.p2; p2.enable(this, Play.DEBUG); // check if obstacle is defined let obstacle = OBSTACLE_DEFS[name]; if (typeof obstacle === "undefined") { console.error("obstacle " + name + " not defined"); return; } let scale = (typeof obstacle.scale !== "undefined") ? obstacle.scale : 1; // change sprite anchor and scale this.anchor.set(frame["anchorX"], frame["anchorY"]); this.scale.set(scale, scale); // set compound collision shapes this.setColliders(obstacle.collisions, scale, flipped); let body = <Phaser.Physics.P2.Body>this.body; body.static = true; // flip sprite if (flipped) { this.scale.x = -1; } //this._sprite.visible = false; //this.alpha = 0.5; // touch point this._touchPoint.set(obstacle.touchPoint.x, obstacle.touchPoint.y); // child sprite //let marker = new Phaser.Sprite(game, this._touchPoint.x, this._touchPoint.y, "Sprites", "Marker"); //marker.anchor.set(0.5, 0.5); //this.addChild(marker); } } Again, ignore touchPoint and tag. What is important: it takes IObstacle definition from OBSTACLE_DEFS and creates colliders for it. I use the same name for frame and for IObstale key in OBSTALE_DEFS. Set anchor and sprite scale before you set colliders. Setting anchor in my example can look weird, but I have atlas frames extended with default anchor - just change it to more traditinal way like: this.anchor.set(0.5, 0.5); Link to comment Share on other sites More sharing options...
Recommended Posts