I'm new to swift and I have been trying to figure out how to use bitmasks and didBegin(_ contact: SKPhysicsContact) to detect when two spaceships touch each other. I can't seem to figure out how.
Here is what I have so far:
import SpriteKit
import GameplayKit
class GameScene: SKScene, SKPhysicsContactDelegate {
let pufferCategory: UInt32 = 1 << 0;
let enemyCategory: UInt32 = 1 << 1;
var spaceship1: SKSpriteNode!
var spaceship2: SKSpriteNode!
override func didMove(to view: SKView) {
spaceship1 = SKSpriteNode(imageNamed: "Spaceship");
spaceship1.setScale(CGFloat(0.1))
spaceship1.position = CGPoint(x: self.frame.width / 2, y: (self.frame.height / 2));
spaceship1.name = "spaceship1";
spaceship1.physicsBody = SKPhysicsBody(circleOfRadius: spaceship1.size.width / 2);
spaceship1.physicsBody?.isDynamic = true // apply gravity, friction, and collision
spaceship1.physicsBody?.affectedByGravity = false;
spaceship1.physicsBody?.allowsRotation = false
spaceship1.physicsBody = SKPhysicsBody(circleOfRadius: spaceship1.size.width / 2);
spaceship1.physicsBody?.categoryBitMask = pufferCategory
spaceship1.physicsBody?.contactTestBitMask = enemyCategory
spaceship2 = SKSpriteNode(imageNamed: "Spaceship");
spaceship2.setScale(CGFloat(0.1))
spaceship2.position = CGPoint(x: self.frame.width / 2, y: (spaceship1.position.y + 300));
spaceship2.name = "puffer2";
spaceship2.physicsBody = SKPhysicsBody(circleOfRadius: spaceship2.size.width / 2);
spaceship2.physicsBody?.isDynamic = true // apply gravity, friction, and collision
spaceship2.physicsBody?.affectedByGravity = false;
spaceship2.physicsBody?.allowsRotation = false
spaceship2.physicsBody = SKPhysicsBody(circleOfRadius: spaceship2.size.width / 2);
spaceship2.physicsBody?.categoryBitMask = enemyCategory
spaceship2.physicsBody?.contactTestBitMask = pufferCategory
addChild(spaceship1)
addChild(spaceship2)
}
func didBegin(_ contact: SKPhysicsContact) {
print("contact")
}
override func update(_ currentTime: TimeInterval) {
spaceship1.physicsBody?.affectedByGravity = false;
}
}
Thanks in advance!
I figured it out!
Here's my code :)
import SpriteKit
import GameplayKit
class GameScene: SKScene, SKPhysicsContactDelegate {
let enemy2Category: UInt32 = 1
let enemyCategory: UInt32 = 2
var spaceship1: SKSpriteNode!
var spaceship2: SKSpriteNode!
override func didMove(to view: SKView) {
self.physicsWorld.contactDelegate = self
spaceship1 = SKSpriteNode(imageNamed: "Spaceship");
spaceship1.setScale(CGFloat(0.1))
spaceship1.position = CGPoint(x: self.frame.width / 2, y: (self.frame.height / 2));
spaceship1.name = "spaceship1";
spaceship1.physicsBody = SKPhysicsBody(circleOfRadius: spaceship1.size.width / 2);
spaceship1.physicsBody?.isDynamic = true // apply gravity, friction, and collision
spaceship1.physicsBody?.affectedByGravity = false;
spaceship1.physicsBody?.allowsRotation = false
spaceship1.physicsBody = SKPhysicsBody(circleOfRadius: spaceship1.size.width / 2);
spaceship1.physicsBody?.categoryBitMask = enemy2Category
spaceship1.physicsBody?.collisionBitMask = enemyCategory
spaceship1.physicsBody?.contactTestBitMask = enemyCategory
spaceship2 = SKSpriteNode(imageNamed: "Spaceship");
spaceship2.setScale(CGFloat(0.1))
spaceship2.position = CGPoint(x: self.frame.width / 2, y: (spaceship1.position.y + 300));
spaceship2.name = "spaceship2";
spaceship2.physicsBody = SKPhysicsBody(circleOfRadius: spaceship2.size.width / 2);
spaceship2.physicsBody?.isDynamic = true // apply gravity, friction, and collision
spaceship2.physicsBody?.affectedByGravity = false;
spaceship2.physicsBody?.allowsRotation = false
spaceship2.physicsBody = SKPhysicsBody(circleOfRadius: spaceship2.size.width / 2);
spaceship2.physicsBody?.categoryBitMask = enemyCategory
spaceship2.physicsBody?.collisionBitMask = enemy2Category
spaceship2.physicsBody?.contactTestBitMask = enemy2Category
addChild(spaceship1)
addChild(spaceship2)
}
func didBegin(_ contact: SKPhysicsContact) {
print("contact")
}
override func update(_ currentTime: TimeInterval) {
spaceship1.physicsBody?.affectedByGravity = false;
}
}
I used this video to help: https://www.youtube.com/watch?v=43hzb4NmQfw
Related
I try to make an endless background through the nodes, but the background has not become infinite and is interrupted, the third background is not yet shown. After the first show, the number of nodes in the scene grows, how can this be fixed?
import SpriteKit
import GameplayKit
class GameScene: SKScene {
var bgNode: SKNode!
var overlay: SKNode!
var overlayWidth: CGFloat!
//var viewSize: CGSize!
var levelPositionX: CGFloat = 0.0
//var speed: CGFloat = 5.5
override func didMove(to view: SKView) {
setupNode()
//viewSize = CGSize(width: frame.size.width, height:
frame.size.height )
}
func setupNode() {
let worldNode = childNode(withName: "World")!
bgNode = worldNode.childNode(withName: "Background")!
overlay = bgNode.childNode(withName: "Overlay")!.copy() as!
SKNode
overlayWidth = overlay.calculateAccumulatedFrame().width
}
func createBackgroundOverlay() {
let backgroundOverlay = overlay.copy() as! SKNode
backgroundOverlay.position = CGPoint(x: 0.0, y: 0.0)
bgNode.addChild(backgroundOverlay)
levelPositionX += overlayWidth
}
func update() {
bgNode.position.x -= 5
if bgNode.position.x <= -self.frame.size.width {
bgNode.position.x = self.frame.size.width * 2
createBackgroundOverlay()
}
}
override func update(_ currentTime: TimeInterval) {
update()
}
In my endless runner game, I have implemented an endless background and a ground(or floor) much similar to your app. Below I shall discuss the steps i have used in my game.
Step 1: In your GameScene.swift file add these variables.
var backgroundSpeed: CGFloat = 80.0 // speed may vary as you like
var deltaTime: TimeInterval = 0
var lastUpdateTimeInterval: TimeInterval = 0
Step 2: In GameScene file, make setUpBackgrouds method as follows
func setUpBackgrounds() {
//add background
for i in 0..<3 {
// add backgrounds, my images were namely, bg-0.png, bg-1.png, bg-2.png
let background = SKSpriteNode(imageNamed: "bg-\(i).png")
background.anchorPoint = CGPoint.zero
background.position = CGPoint(x: CGFloat(i) * size.width, y: 0.0)
background.size = self.size
background.zPosition = -5
background.name = "Background"
self.addChild(background)
}
for i in 0..<3 {
// I have used one ground image, you can use 3
let ground = SKSpriteNode(imageNamed: "Screen.png")
ground.anchorPoint = CGPoint(x: 0, y: 0)
ground.size = CGSize(width: self.size.width, height: ground.size.height)
ground.position = CGPoint(x: CGFloat(i) * size.width, y: 0)
ground.zPosition = 1
ground.name = "ground"
self.addChild(ground)
}
}
Step 3: Now we have to capture timeIntervals from update method
override func update(_ currentTime: TimeInterval) {
if lastUpdateTimeInterval == 0 {
lastUpdateTimeInterval = currentTime
}
deltaTime = currentTime - lastUpdateTimeInterval
lastUpdateTimeInterval = currentTime
}
Step 4: Here comes the most important part, moving our backgrounds and groungFloor by enumerating child nodes. Add these two methods in GameScene.swift file.
func updateBackground() {
self.enumerateChildNodes(withName: "Background") { (node, stop) in
if let back = node as? SKSpriteNode {
let move = CGPoint(x: -self.backgroundSpeed * CGFloat(self.deltaTime), y: 0)
back.position += move
if back.position.x < -back.size.width {
back.position += CGPoint(x: back.size.width * CGFloat(3), y: 0)
}
}
}
}
func updateGroundMovement() {
self.enumerateChildNodes(withName: "ground") { (node, stop) in
if let back = node as? SKSpriteNode {
let move = CGPoint(x: -self.backgroundSpeed * CGFloat(self.deltaTime), y: 0)
back.position += move
if back.position.x < -back.size.width {
back.position += CGPoint(x: back.size.width * CGFloat(3), y: 0)
}
}
}
}
Step 5: At this point you should get this error:"Binary operator '+=' cannot be applied to two 'CGPoint' operands" in updateBackground and updateGroundMovement methods.
Now we need to implement operator overloading to resolve this problem. Create a new Swift File and name it Extensions.swift and then implement as follows:
// Extensions.swift
import CoreGraphics
import SpriteKit
public func + (left: CGPoint, right: CGPoint) -> CGPoint {
return CGPoint(x: left.x + right.x, y: left.y + right.y)
}
public func += (left: inout CGPoint, right: CGPoint) {
left = left + right
}
Step 6: call setUpBackgrounds method in didMove(toView:)
override func didMove(to view: SKView) {
setUpBackgrounds()
}
Step 7: Finally call the updateBackground and updateGroundMovement methods in update(_ currentTime) method. updated code is given below:
override func update(_ currentTime: TimeInterval) {
if lastUpdateTimeInterval == 0 {
lastUpdateTimeInterval = currentTime
}
deltaTime = currentTime - lastUpdateTimeInterval
lastUpdateTimeInterval = currentTime
//MARK:- Last step:- add these methods here
updateBackground()
updateGroundMovement()
}
I want to make a game when it's game over, a banner ad shows up, but I found out that the view takes at least one minute to load. I tried doing this on a different thread but it didn't work. I created the view in GameViewController.swift, and added the subview in the GameScene.swift. Also the Game Over Pop up is a set of SKSpriteNodes and SKLabelNodes.
GameViewController.swift
class GameViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
print("READY__$$$$$$$$$$$$$$$$$$$_________________.")
banner = GADBannerView(adSize: kGADAdSizeFullBanner)
banner.adUnitID = "ca-app-pub-3940256099942544/2934735716"
let request = GADRequest()
banner.load(request)
banner.frame = CGRect(x: 0, y: (view?.bounds.height)! - banner.frame.size.height, width: banner.frame.size.width, height: banner.frame.size.height)
banner.rootViewController = self
if let view = self.view as! SKView? {
// Load the SKScene from 'GameScene.sks'
if let scene = SKScene(fileNamed: "IntroScene") {
// Set the scale mode to scale to fit the window
scene.scaleMode = .aspectFill
// Present the scene
view.presentScene(scene)
}
view.ignoresSiblingOrder = true
view.showsFPS = true
view.showsNodeCount = true
}
}
override var shouldAutorotate: Bool {
return true
}
override var supportedInterfaceOrientations: UIInterfaceOrientationMask {
if UIDevice.current.userInterfaceIdiom == .phone {
return .allButUpsideDown
} else {
return .all
}
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Release any cached data, images, etc that aren't in use.
}
override var prefersStatusBarHidden: Bool {
return true
}
}
GameScene.swift
func spawnAd() {
print("READY___________________.")
DispatchQueue.main.async(execute: {
self.view?.addSubview(banner)
})
// DispatchQueue.global(qos: .userInteractive).async {
// DispatchQueue.main.async {
// self.view?.addSubview(banner)
// }
//
// }
}
The Game Over Screen Pop up
func spawnGameOverMenu() {
let dimPanel = SKSpriteNode(color: UIColor.brown, size: self.size)
dimPanel.alpha = 0.0
dimPanel.zPosition = 9
dimPanel.position = CGPoint(x: self.frame.midX, y: self.frame.midY)
self.addChild(dimPanel)
let fadeAlpha = SKAction.fadeAlpha(by: 0.5, duration: 0.6)
dimPanel.run(fadeAlpha)
gameOverMenu = SKSpriteNode(color: bgColor, size: CGSize(width: self.frame.width - 5, height: self.frame.height / 2))
gameOverMenu.position = CGPoint(x: self.frame.midX, y: self.frame.minY - self.frame.height)
gameOverMenu.name = "gameOverMenu"
gameOverMenu.zPosition = 10
self.addChild(gameOverMenu)
spawnGameOverLabel()
spawnResetLbl()
spawnAd()
let moveUp = SKAction.moveTo(y: self.frame.midY, duration: 1.0)
gameOverMenu.run(moveUp)
}
I figured out why it wasn't working, I set the root view controller after I loaded the request.
banner = GADBannerView(adSize: kGADAdSizeFullBanner)
banner.adUnitID = "ca-app-pub-3940256099942544/2934735716"
banner.rootViewController = self
let request = GADRequest()
banner.load(request)
banner.frame = CGRect(x: 0, y: (view?.bounds.height)! - banner.frame.size.height, width: banner.frame.size.width, height: banner.frame.size.height)
I am having trouble figuring out how to differentiate between different bitmasks.
I want this to happen:
/*func didBegin(_ contact: SKPhysicsContact) {
if (spaceship1 collides with spaceship2) {
print("contact between 1 and 2")
}
if (spaceship1 collides with spaceship3) {
print("contact between 1 and 3")
}
}
*/
Here's the code I have tried:
func didBegin(_ contact: SKPhysicsContact) {
if (contact.bodyA.categoryBitMask = enemyCategory && contact.bodyB.categoryBitMask = enemy2Category) {
print("contact between 1 and 2")
}
}
Could I get some help?
EDIT: Here's the other code:
import SpriteKit
import GameplayKit
class GameScene: SKScene, SKPhysicsContactDelegate {
let enemy2Category: UInt32 = 1
let enemyCategory: UInt32 = 2
let enemy3Category: UInt32 = 3
var spaceship1: SKSpriteNode!
var spaceship2: SKSpriteNode!
var spaceship3: SKSpriteNode!
override func didMove(to view: SKView) {
self.physicsWorld.contactDelegate = self
spaceship1 = SKSpriteNode(imageNamed: "Spaceship");
spaceship1.setScale(CGFloat(0.1))
spaceship1.position = CGPoint(x: self.frame.width / 2, y: (self.frame.height / 2));
spaceship1.name = "spaceship1";
spaceship1.physicsBody = SKPhysicsBody(circleOfRadius: spaceship1.size.width / 2);
spaceship1.physicsBody?.isDynamic = true // apply gravity, friction, and collision
spaceship1.physicsBody?.affectedByGravity = true;
spaceship1.physicsBody?.allowsRotation = false
spaceship1.physicsBody = SKPhysicsBody(circleOfRadius: spaceship1.size.width / 2);
spaceship1.physicsBody?.categoryBitMask = enemy2Category
spaceship1.physicsBody?.collisionBitMask = enemyCategory
spaceship1.physicsBody?.contactTestBitMask = enemyCategory
spaceship2 = SKSpriteNode(imageNamed: "Spaceship");
spaceship2.setScale(CGFloat(0.1))
spaceship2.position = CGPoint(x: self.frame.width / 2, y: (spaceship1.position.y + 300));
spaceship2.name = "spaceship2";
spaceship2.physicsBody = SKPhysicsBody(circleOfRadius: spaceship2.size.width / 2);
spaceship2.physicsBody?.isDynamic = true // apply gravity, friction, and collision
spaceship2.physicsBody?.affectedByGravity = false;
spaceship2.physicsBody?.allowsRotation = false
spaceship2.physicsBody = SKPhysicsBody(circleOfRadius: spaceship2.size.width / 2);
spaceship2.physicsBody?.categoryBitMask = enemyCategory
spaceship2.physicsBody?.collisionBitMask = enemy2Category
spaceship2.physicsBody?.contactTestBitMask = enemy2Category
spaceship3 = SKSpriteNode(imageNamed: "Spaceship");
spaceship3.setScale(CGFloat(0.1))
spaceship3.position = CGPoint(x: self.frame.width / 2, y: (spaceship1.position.y + 300));
spaceship3.name = "spaceship3";
spaceship3.physicsBody = SKPhysicsBody(circleOfRadius: spaceship2.size.width / 2);
spaceship3.physicsBody?.isDynamic = true // apply gravity, friction, and collision
spaceship3.physicsBody?.affectedByGravity = false;
spaceship3.physicsBody?.allowsRotation = false
spaceship3.physicsBody = SKPhysicsBody(circleOfRadius: spaceship2.size.width / 2);
spaceship3.physicsBody?.categoryBitMask = enemy3Category
spaceship3.physicsBody?.collisionBitMask = enemy2Category | enemyCategory
spaceship3.physicsBody?.contactTestBitMask = enemy2Category | enemyCategory
addChild(spaceship1)
}
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
addChild(spaceship3)
}
override func update(_ currentTime: TimeInterval) {
spaceship1.physicsBody?.affectedByGravity = false;
}
}
I'm just trying to figure out how to detect which bitmasks come into contact with each other. Like if spaceship1 comes into contact with spaceship2, it prints yay, but if spaceship1 comes into contact with spaceship 3, it prints "wow"
There is a documentation article that tries to explain bit masks as used in Sprite-Kit collision and contacts, but I can no longer link to it. Try to find the 'Documentation' page, tag 'Sprite-Kit' then the 'Manipulating contactTest and collison bitmasks to enable/disable specific contact and collisions.' article under 'SKNode Collision'.
One obvious problem is this line :
let enemy3Category: UInt32 = 3
All bitmasks have to be thought of in binary, and in binary 3 is 11. This is the '1' bit and the '2' bit both being set to 1. Category definitions should only have 1 bit set to 1 which you ensure by using exact powers of 2 for your categories - 1, 2, 4, 8, 16, 32, etc.
Objects in your scene that need to collide/contact should belong to only one category initially. For more advanced contacts and collisions, a sprite can belong to multiple categories.
You are missing an = in your if statement.
func didBegin(_ contact: SKPhysicsContact) {
if (contact.bodyA.categoryBitMask == enemyCategory && contact.bodyB.categoryBitMask == enemy2Category) {
print("contact between 1 and 2")
}
}
This sets firstBody to the smaller bitmask And sets secondBody to the larger one
var firstBody = SKPhysicsBody()
var secondBody = SKPhysicsBody()
if contact.bodyA.categoryBitMask < contact.bodyB.categoryBitMask {
firstBody = contact.bodyA
secondBody = contact.bodyB
}else{
firstBody = contact.bodyB
secondBody = contact.bodyA
}
Assuiming bitmask1 is smaller than 2 and 3.
if firstBody.categoryBitMask == bitMask1 && secondBody.categoryBitMask == bitMask.bitmask3 {
print("a")
//If ball hits a pad, activate the pad
}else if firstBody.categoryBitMask == bitMask1 && secondBody.categoryBitMask == bitMask.bitmask2 {
print("e")
}
how do we create a UICollectionViewLayout like the SnapChat's stories?
Any ideas please?
I'd like to have a solution without external library.
Based on a precedent answer adapted to your issue:
-(id)initWithSize:(CGSize)size
{
self = [super init];
if (self)
{
_unitSize = CGSizeMake(size.width/2,80);
_cellLayouts = [[NSMutableDictionary alloc] init];
}
return self;
}
-(void)prepareLayout
{
for (NSInteger aSection = 0; aSection < [[self collectionView] numberOfSections]; aSection++)
{
//Create Cells Frames
for (NSInteger aRow = 0; aRow < [[self collectionView] numberOfItemsInSection:aSection]; aRow++)
{
NSIndexPath *indexPath = [NSIndexPath indexPathForItem:aRow inSection:aSection];
UICollectionViewLayoutAttributes *attributes = [UICollectionViewLayoutAttributes layoutAttributesForCellWithIndexPath:indexPath];
NSUInteger i = aRow%3;
NSUInteger j = aRow/3;
CGFloat offsetY = _unitSize.height*2*j;
CGPoint xPoint;
CGFloat height = 0;
BOOL invert = NO;
if (aRow%6 >= 3) //We need to invert Big cell and small cells => xPoint.x
{
invert = YES;
}
switch (i)
{
case 0:
xPoint = CGPointMake((invert?_unitSize.width:0), offsetY);
height = _unitSize.height;
break;
case 1:
xPoint = CGPointMake((invert?_unitSize.width:0), offsetY+_unitSize.height);
height = _unitSize.height;
break;
case 2:
xPoint = CGPointMake((invert?0:_unitSize.width), offsetY);
height = _unitSize.height*2;
break;
default:
break;
}
CGRect frame = CGRectMake(xPoint.x, xPoint.y, _unitSize.width, height);
[attributes setFrame:frame];
[_cellLayouts setObject:attributes forKey:indexPath];
}
}
}
I set the height of unitSize to 80, but you can use the size of the screen if needed, like _unitSize = CGSizeMake(size.width/2,size.height/4.);.
That render:
Side note: It's up to you to adapt the logic, or do changes, the cell frames calculation may not be the "best looking piece of code".
UICollectionViewLayout like the SnapChat's stories Like
Swift 3.2 Code
import Foundation
import UIKit
class StoryTwoColumnsLayout : UICollectionViewLayout {
fileprivate var cache = [IndexPath: UICollectionViewLayoutAttributes]()
fileprivate var cellPadding: CGFloat = 4
fileprivate var contentHeight: CGFloat = 0
var oldBound: CGRect!
let numberOfColumns:Int = 2
var cellHeight:CGFloat = 255
fileprivate var contentWidth: CGFloat {
guard let collectionView = collectionView else {
return 0
}
let insets = collectionView.contentInset
return collectionView.bounds.width - (insets.left + insets.right)
}
override var collectionViewContentSize: CGSize {
return CGSize(width: contentWidth, height: contentHeight)
}
override func prepare() {
super.prepare()
contentHeight = 0
cache.removeAll(keepingCapacity: true)
guard cache.isEmpty == true, let collectionView = collectionView else {
return
}
if collectionView.numberOfSections == 0 {
return
}
let cellWidth = contentWidth / CGFloat(numberOfColumns)
cellHeight = cellWidth / 720 * 1220
var xOffset = [CGFloat]()
for column in 0 ..< numberOfColumns {
xOffset.append(CGFloat(column) * cellWidth)
}
var column = 0
var yOffset = [CGFloat](repeating: 0, count: numberOfColumns)
for item in 0 ..< collectionView.numberOfItems(inSection: 0) {
let indexPath = IndexPath(item: item, section: 0)
var newheight = cellHeight
if column == 0 {
newheight = ((yOffset[column + 1] - yOffset[column]) > cellHeight * 0.3) ? cellHeight : (cellHeight * 0.90)
}
let frame = CGRect(x: xOffset[column], y: yOffset[column], width: cellWidth, height: newheight)
let insetFrame = frame.insetBy(dx: cellPadding, dy: cellPadding)
let attributes = UICollectionViewLayoutAttributes(forCellWith: indexPath)
attributes.frame = insetFrame
cache[indexPath] = (attributes)
contentHeight = max(contentHeight, frame.maxY)
yOffset[column] = yOffset[column] + newheight
if column >= (numberOfColumns - 1) {
column = 0
} else {
column = column + 1
}
}
}
override func layoutAttributesForElements(in rect: CGRect) -> [UICollectionViewLayoutAttributes]? {
var visibleLayoutAttributes = [UICollectionViewLayoutAttributes]()
// Loop through the cache and look for items in the rect
visibleLayoutAttributes = cache.values.filter({ (attributes) -> Bool in
return attributes.frame.intersects(rect)
})
print(visibleLayoutAttributes)
return visibleLayoutAttributes
}
override func layoutAttributesForItem(at indexPath: IndexPath) -> UICollectionViewLayoutAttributes? {
// print(cache[indexPath.item])
return cache[indexPath]
}
}
I am building a site which requires a physics engine.
Having worked with a number of SPA apps i feel pretty confident with.
Unfortunately I am having trouble applying collision detection & walls to a physics simulation that famous has created.
You can see the editable example here.
https://staging.famous.org/examples/index.html?block=gravity3d&detail=false&header=false
What I would like to know is it possible to add collisions to the particles? I have tried this but it appears the collisions are not setup correctly. I was hoping someone has managed to do it successfully.
Thanks!
var FamousEngine = famous.core.FamousEngine;
var Camera = famous.components.Camera;
var DOMElement = famous.domRenderables.DOMElement;
var Gravity3D = famous.physics.Gravity3D;
var Gravity1D = famous.physics.Gravity1D;
var MountPoint = famous.components.MountPoint;
var PhysicsEngine = famous.physics.PhysicsEngine;
var Physics = famous.physics;
var Wall = famous.physics.Wall;
var Position = famous.components.Position;
var Size = famous.components.Size;
var Sphere = famous.physics.Sphere;
var Vec3 = famous.math.Vec3;
var Collision = famous.physics.Collision;
function Demo() {
this.scene = FamousEngine.createScene('#socialInteractive');
this.camera = new Camera(this.scene);
this.camera.setDepth(1000);
this.simulation = new PhysicsEngine();
this.items = [];
this.collision = new Collision();
var Wall = famous.physics.Wall;
var rightWall = new Wall({
direction: Wall.LEFT
}); // bodies coming from the left will collide with the wall
rightWall.setPosition(1000, 0, 0);
var ceiling = new Wall({
normal: [0, 1, 0],
distance: 300,
restitution: 0
});
var floor = new Wall({
normal: [0, -1, 0],
distance: 300,
restitution: 0
});
var left = new Wall({
normal: [1, 0, 0],
distance: 350,
restitution: 0
});
var right = new Wall({
normal: [-1, 0, 0],
distance: 350,
restitution: 0
});
var node = this.scene.addChild();
var position = new Position(node);
// this.simulation.attach([right, left, floor, ceiling])
// this.items.push([ceiling,position]);
for (var i = 0; i < 10; i++) {
var node = this.scene.addChild();
var size = new Size(node).setMode(1, 1);
var position = new Position(node);
if (i === 0) {
createLogo.call(this, node, size, position);
}
if (i !== 0) {
node.id = i;
createSatellites.call(this, node, size, position);
}
}
FamousEngine.requestUpdateOnNextTick(this);
console.log(this.collision)
for (var i = 0; i < this.collision.length; i++) {
this.simulation.attach(collision, balls, balls[i]);
}
this.simulation.addConstraint(this.collision);
}
Demo.prototype.onUpdate = function(time) {
this.simulation.update(time);
this.collision.update(time, 60);
if (this.items.length > 0) {
for (var i = 0; i < this.items.length; i++) {
var itemPosition = this.simulation.getTransform(this.items[i][0]).position;
this.items[i][1].set(itemPosition[0], itemPosition[1], 0);
}
}
FamousEngine.requestUpdateOnNextTick(this);
};
function createLogo(node, size, position) {
size.setAbsolute(50, 50);
var mp = new MountPoint(node).set(0.5, 0.5);
var el = new DOMElement(node, {
tagName: 'img',
attributes: {
src: './images/famous-logo.svg'
}
});
var sphere = new Sphere({
radius: 100,
mass: 10000,
restrictions: ['xy'],
position: new Vec3(window.innerWidth / 2, window.innerHeight / 2, 5)
});
this.gravity = new Gravity3D(sphere);
this.simulation.add(sphere, this.gravity);
this.items.push([sphere, position]);
}
function createSatellites(node, size, position, i) {
size.setAbsolute(20, 20);
var radius = 200;
var x = Math.floor(Math.random() * radius * 2) - radius;
var y = (Math.round(Math.random()) * 2 - 1) * Math.sqrt(radius * radius - x * x);
var color = 'rgb(' + Math.abs(x) + ',' + Math.abs(Math.round(y)) + ',' + (255 - node.id) + ')';
var el = new DOMElement(node, {
properties: {
'background-color': color,
'border-radius': '50%'
}
});
var satellite = new Sphere({
radius: 20,
mass: 5,
position: new Vec3(x + window.innerWidth / 2, y + window.innerHeight / 2, 0)
});
satellite.setVelocity(-y / Math.PI, -x / Math.PI / 2, y / 2);
this.gravity.addTarget(satellite);
this.simulation.add(satellite);
this.items.push([satellite, position]);
this.collision.addTarget(satellite);
}
// Boilerplate
FamousEngine.init();
// App Code
var demo = new Demo();
Thanks for your help Talves, I think i found a solution that works with physics Spring instead of gravity3d. This also has the 4 walls included which seems to work well.
var famous = famous;
var FamousEngine = famous.core.FamousEngine;
var Camera = famous.components.Camera;
var DOMElement = famous.domRenderables.DOMElement;
var Gravity3D = famous.physics.Gravity3D;
var MountPoint = famous.components.MountPoint;
var PhysicsEngine = famous.physics.PhysicsEngine;
var Position = famous.components.Position;
var Size = famous.components.Size;
var Wall = famous.physics.Wall;
var Sphere = famous.physics.Sphere;
var Vec3 = famous.math.Vec3;
var math = famous.math;
var physics = famous.physics;
var collision = famous.physics.Collision;
var gestures = famous.components.GestureHandler;
var Spring = famous.physics.Spring;
console.log(famous)
var anchor = new Vec3(window.innerWidth / 2, window.innerHeight / 2, 0);
//Create Walls
var rightWall = new Wall({
direction: Wall.LEFT
}).setPosition(window.innerWidth - 20, 0, 0);
var leftWall = new Wall({
direction: Wall.RIGHT
}).setPosition(window.innerWidth + 20, 0, 0);
var topWall = new Wall({
direction: Wall.DOWN
}).setPosition(0, 20, 0);
var bottomWall = new Wall({
direction: Wall.UP
}).setPosition(0, window.innerHeight - 20, 0);
function Demo() {
this.scene = FamousEngine.createScene('body');
this.camera = new Camera(this.scene);
this.camera.setDepth(1000);
this.collision = new collision([rightWall, leftWall, topWall, bottomWall]);
this.simulation = new PhysicsEngine();
this.simulation.setOrigin(0.5, 0.5);
this.simulation.addConstraint(this.collision);
this.items = [];
this.walls = [];
//Create Items
for (var i = 0; i < 30; i++) {
var node = this.scene.addChild();
node.setMountPoint(0.5, 0.5);
var size = new Size(node).setMode(1, 1);
var position = new Position(node);
if (i === 0) {
createLogo.call(this, node, size, position);
}
if (i !== 0) {
node.id = i;
createSatellites.call(this, node, size, position);
}
}
//Create Walls
var node = this.scene.addChild();
createWalls(node);
FamousEngine.requestUpdateOnNextTick(this);
Demo.prototype.onUpdate = function(time) {
this.simulation.update(time);
//Postition walls
var wallPosition = topWall.getPosition();
node.setPosition(wallPosition.x, wallPosition.y);
//Position elements
if (this.items.length > 0) {
for (var i = 0; i < this.items.length; i++) {
var itemPosition = this.simulation.getTransform(this.items[i][0]).position;
this.items[i][1].set(itemPosition[0], itemPosition[1], 0);
}
}
FamousEngine.requestUpdateOnNextTick(this);
};
}
function createWalls(wallNode) {
wallNode.setSizeMode('absolute', 'absolute', 'absolute').setAbsoluteSize(window.innerWidth, 10, 0);
var wallDOMElement = new DOMElement(wallNode, {
tagName: 'div'
}).setProperty('background-color', 'lightblue');
}
function createLogo(node, size, position) {
size.setAbsolute(50, 50);
var mp = new MountPoint(node).set(0.5, 0.5);
var el = new DOMElement(node, {
tagName: 'img',
attributes: {
src: './images/famous_logo.png'
}
});
var sphere = new Sphere({
radius: 100,
mass: 10000,
restrictions: ['xy'],
position: new Vec3(window.innerWidth / 2, window.innerHeight / 2, 5)
});
// this.gravity = new Gravity3D(sphere);
// this.simulation.add(sphere, this.gravity);
this.simulation.add(sphere);
this.items.push([sphere, position]);
}
function createSatellites(node, size, position, i) {
size.setAbsolute(50, 50);
var radius = 100;
var x = Math.floor(Math.random() * radius * 2) - radius;
var y = (Math.round(Math.random()) * 2 - 1) * Math.sqrt(radius * radius - x * x);
var color = 'rgb(' + Math.abs(x) + ',' + Math.abs(Math.round(y)) + ',' + (255 - node.id) + ')';
var el = new DOMElement(node, {
properties: {
'background-color': color,
'border-radius': '50%'
}
});
var satellite = new Sphere({
radius: 25,
mass: 10,
position: new Vec3(x + window.innerWidth / 2, y + window.innerHeight / 2, 0)
});
// Attach the box to the anchor with a `Spring` force
var spring = new Spring(null, satellite, {
stiffness: 95,
period: 0.6,
dampingRatio: 1.0,
anchor: anchor
});
//console.log(color);
// satellite.setVelocity(-y / Math.PI, -x / Math.PI / 2, y / 2);
satellite.setVelocity(0.5, 0.5, 0);
// this.gravity.addTarget(satellite);
this.simulation.add(satellite, spring);
this.items.push([satellite, position]);
this.collision.addTarget(satellite);
}
// Boilerplate
FamousEngine.init();
// App Code
var demo = new Demo();
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<title>Famous :: Seed Project</title>
<link rel="icon" href="favicon.ico?v=1" type="image/x-icon">
<meta name="description" content="">
<meta name="viewport" content="width=device-width, initial-scale=1">
<style>
html, body {
width: 100%;
height: 100%;
margin: 0px;
padding: 0px;
}
body {
position: absolute;
-webkit-transform-style: preserve-3d;
transform-style: preserve-3d;
-webkit-font-smoothing: antialiased;
-webkit-tap-highlight-color: transparent;
-webkit-perspective: 0;
perspective: none;
overflow: hidden;
}
</style>
</head>
<body>
<script src="http://code.famo.us/famous/0.6.2/famous.min.js"></script>
</body>
</html>