I'm trying to build a screen with circular bodies (bubbles). The bubbles should be able to change the scale with some interval, e.g. every 3 seconds and random value (from 1 to 3). The scale should change the size of a bubble and its mass.
Calling
Matter.Body.scale
doesn't make any effect. Bubbles keep staying the same. Maybe because I'm not using sprites with textures, but the Game-Engine entities with renderer as React.PureComponent instead. Not sure
Bubble node:
export interface BubbleProps {
body: Matter.Body;
item: Item;
}
class BubbleNode extends React.Component<BubbleProps> {
render() {
const {body, item} = this.props;
const x = body.position.x - body.circleRadius;
const y = body.position.y - body.circleRadius;
const style: ViewStyle = {
position: 'absolute',
left: x,
top: y,
width: body.circleRadius * 2,
height: body.circleRadius * 2,
backgroundColor: item.color,
borderRadius: body.circleRadius,
alignItems: 'center',
justifyContent: 'center',
overflow: 'hidden',
padding: 3,
};
return <View style={style}>{item.content}</View>;
}
}
Bubble Entity:
const body = Matter.Bodies.circle(
randomX,
randomY,
RADIUS, //default radius 25
{
mass: 30,
}
);
return {
body,
item,
renderer: BubbleNode,
};
Update method:
// find out if the entity was already added to the world
if (Object.prototype.hasOwnProperty.call(entities, id)) {
//update scale
const scale = bubble.item.scale
Matter.Body.scale(bubble.body, scale, scale)
} else {
//add new entity
entities[id] = bubble;
Matter.World.add(physics.world, [bubble.body]);
}
Also, I need to make this scaling smooth
Edit:
Interesting thing. I was able to scale the bubbles using Matter.Body.scale but right before adding to the world. I wonder if there is a method to update the bodies after adding to the world.
Final edit
this is the method I'm using to set up the world and specify entities for the GameEngine framework
private setupWorld = (layout: LayoutRectangle) => {
const engine = Matter.Engine.create({enableSleeping: false});
const world = engine.world;
world.gravity.x = 0;
world.gravity.y = 0;
//center gravity body
Matter.use(MatterAttractors);
var attractiveBody = Matter.Bodies.circle(
layout.width / 2,
layout.height / 2,
1,
{
isSensor: true,
plugin: {
attractors: [centerGravity],
},
},
);
Matter.World.add(world, attractiveBody);
//walls
const wallThickness = 5;
let floor = Matter.Bodies.rectangle(
layout.width / 2,
layout.height - wallThickness / 2,
layout.width,
wallThickness,
{isStatic: true},
);
let ceiling = Matter.Bodies.rectangle(
layout.width / 2,
wallThickness / 2,
layout.width,
wallThickness,
{isStatic: true},
);
let left = Matter.Bodies.rectangle(
wallThickness / 2,
layout.height / 2,
wallThickness,
layout.height,
{isStatic: true},
);
let right = Matter.Bodies.rectangle(
layout.width - wallThickness / 2,
layout.height / 2,
wallThickness,
layout.height,
{isStatic: true},
);
Matter.World.add(world, [floor, ceiling, left, right]);
//basic entitites
this.entities = {
physics: {engine, world},
floor: {body: floor, color: 'green', renderer: Wall},
ceiling: {body: ceiling, color: 'green', renderer: Wall},
left: {body: left, color: 'green', renderer: Wall},
right: {body: right, color: 'green', renderer: Wall},
};
};
And here is the method that will be triggered by a parent Component with some interval
public updateNodes = (items: Item[]) => {
if (!this.state.mounted || !this.entities || !this.entities.physics || !this.layout) {
console.log('Missiing required data');
return;
}
const layout = this.layout
const entities = this.entities
const bubbles: BubbleEntity[] = items.map((item) => {
const randomX = randomPositionValue(layout.width);
const randomY = randomPositionValue(layout.height);
const body = Matter.Bodies.circle(
randomX,
randomY,
RADIUS, {
mass: 30,
}
);
body.label = item.id
return {
body,
item,
renderer: BubbleNode,
};
});
const physics = this.entities.physics as PhysicsEntity;
const allBodies = Matter.Composite.allBodies(physics.world)
bubbles.forEach((bubble) => {
//update existing or add new
const id = `bubble#${bubble.item.id}`;
if (Object.prototype.hasOwnProperty.call(entities, id)) {
//do physical node update here
//update scale and mass
const scale = bubble.item.scale
console.log('Updating item', id, scale);
//right her there used to be an issue because I used **bubble.body** which was not a correct reference to the world's body.
//so when I started to use allBodies array to find a proper reference of the body, everything started to work
let body = allBodies.find(item => item.label === bubble.item.id)
if (!!body) {
const scaledRadius = RADIUS*scale
const current = body.circleRadius || RADIUS
const scaleValue = scaledRadius/current
Matter.Body.scale(body, scaleValue, scaleValue)
}else{
console.warn('Physycal body not found, while the entity does exist');
}
} else {
console.log('Adding entity to the world');
entities[id] = bubble;
Matter.World.add(physics.world, [bubble.body]);
}
});
this.entities = entities
};
In the future, I'm going to improve that code, I will use some variables for the body and will create a matter.js plugin that will allow me to scale the body smoothly and not instant as it works right now. Also, the method above requires some clean, short implementation instead of that garbage I made attempting to make it work