File: ViewController.m

package info (click to toggle)
chipmunk 5.3.4-1
  • links: PTS
  • area: main
  • in suites: wheezy
  • size: 6,552 kB
  • sloc: ansic: 10,469; objc: 1,845; ruby: 279; perl: 108; makefile: 62; sh: 20
file content (166 lines) | stat: -rw-r--r-- 7,201 bytes parent folder | download
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
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
#import "ViewController.h"

#import "SimpleSound.h"

// An object to use as a collision type for the screen border.
// Class objects and strings are easy and convenient to use as collision types.
static NSString *borderType = @"borderType";

@implementation ViewController

- (void)viewDidLoad {
	[super viewDidLoad];
	
	// Create and initialize the Chipmunk space.
	// Chipmunk spaces are containers for simulating physics.
	// You add a bunch of objects and joints to the space and the space runs the physics simulation on them all together.
	space = [[ChipmunkSpace alloc] init];
	
	// This method adds four static line segment shapes to the space.
	// Most 2D physics games end up putting all the gameplay in a box. This method just makes that easy.
	// We'll tag these segment shapes with the borderType object. You'll see what this is for next.
	[space addBounds:self.view.bounds thickness:10.0f elasticity:1.0f friction:1.0f layers:CP_ALL_LAYERS group:CP_NO_GROUP collisionType:borderType];
	
	// This adds a callback that happens whenever a shape tagged with the
	// [FallingButton class] object and borderType objects collide.
	// You can use any object you want as a collision type identifier.
	// I often find it convenient to use class objects to define collision types.
	// There are 4 different collision events that you can catch: begin, pre-solve, post-solve and separate.
	// See the documentation for a description of what they are all for.
	[space addCollisionHandler:self
		typeA:[FallingButton class] typeB:borderType
		begin:@selector(beginCollision:space:)
		preSolve:nil
		postSolve:@selector(postSolveCollision:space:)
		separate:@selector(separateCollision:space:)
	];
	
	
	// You have to actually put things in a Chipmunk space for it to do anything interesting.
	// I've created a game object controller class called FallingButton that makes a UIButton that is influenced by physics.
	fallingButton = [[FallingButton alloc] init];
	// Add the UIButton to the view hierarchy.
	[self.view addSubview:fallingButton.button];
	
	// Adding physics objects in Objective-Chipmunk is a snap thanks to the ChipmunkObject protocol.
	// No matter how many physics objects (collision shapes, joints, etc) the fallingButton has, it can be added in a single line!
	// See FallingButton.m to see how easy it is to implement the protocol.
	[space add:fallingButton];
}



- (bool)beginCollision:(cpArbiter*)arbiter space:(ChipmunkSpace*)space {
	// This macro gets the colliding shapes from the arbiter and defines variables for them.
	CHIPMUNK_ARBITER_GET_SHAPES(arbiter, buttonShape, border);
	
	// It expands to look something like this:
	// ChipmunkShape *buttonShape = GetShapeWithFirstCollisionType();
	// ChipmunkShape *border = GetShapeWithSecondCollisionType();
	
	// Lets log the data pointers just to make sure we are getting what we think we are.
	NSLog(@"First object in the collision is %@ second object is %@.", buttonShape.data, border.data);
	
	// When we created the collision shape for the FallingButton,
	// we set the data pointer to point at the FallingButton it was associated with.
	FallingButton *fb = buttonShape.data;
	
	// Increment the touchedShapes counter on the FallingButton object.
	// We'll decrement this in the separate callback.
	// If the counter is 0, then you know you aren't touching anything.
	// You can use this technique in platformer games to track if the player is in the air on not.
	fb.touchedShapes++;
	
	// Change the background color to gray so we know when the button is touching something.
	self.view.backgroundColor = [UIColor grayColor];
	
	// begin and pre-solve callbacks MUST return a boolean.
	// Returning false from a begin callback ignores a collision permanently.
	// Returning false from a pre-solve callback ignores the collision for just one frame.
	// See the documentation on collision handlers for more information.
	return TRUE; // We return true, so the collision is handled normally.
}

// The post-solve collision callback is called right after Chipmunk has finished calculating all of the
// collision responses. You can use it to find out how hard objects hit each other.
// There is also a pre-solve callback that allows you to reject collisions conditionally.
- (void)postSolveCollision:(cpArbiter*)arbiter space:(ChipmunkSpace*)space {
	// We only care about the first frame of the collision.
	// If the shapes have been colliding for more than one frame, return early.
	if(!cpArbiterIsFirstContact(arbiter)) return;
	
	// This method gets the impulse that was applied between the two objects to resolve
	// the collision. We'll use the length of the impulse vector to approximate the sound
	// volume to play for the collision.
	cpFloat impulse = cpvlength(cpArbiterTotalImpulse(arbiter));
	
	float volume = MIN(impulse/500.0f, 1.0f);
	if(volume > 0.05f){
		[SimpleSound playSoundWithVolume:volume];
	}
}

static CGFloat frand(){return (CGFloat)rand()/(CGFloat)RAND_MAX;}

// The separate callback is called whenever shapes stop touching.
- (void)separateCollision:(cpArbiter*)arbiter space:(ChipmunkSpace*)space {
	CHIPMUNK_ARBITER_GET_SHAPES(arbiter, buttonShape, border);
	
	// Decrement the counter on the FallingButton.
	FallingButton *fb = buttonShape.data;
	fb.touchedShapes--;
	
	// If touchedShapes is 0, then we know the falling button isn't touching anything anymore.
	if(fb.touchedShapes == 0){
		// Let's set the background color to a random color so you can see each time the shape touches something new.
		self.view.backgroundColor = [UIColor colorWithRed:frand() green:frand() blue:frand() alpha:1.0f];
	}
}

// When the view appears on the screen, start the animation timer and tilt callbacks.
- (void)viewDidAppear:(BOOL)animated {
	// Set up the display link to control the timing of the animation.
	displayLink = [CADisplayLink displayLinkWithTarget:self selector:@selector(update)];
	displayLink.frameInterval = 1;
	[displayLink addToRunLoop:[NSRunLoop mainRunLoop] forMode:NSDefaultRunLoopMode];
	
	// Set up an accelerometer delegate.
	UIAccelerometer *accel = [UIAccelerometer sharedAccelerometer];
	accel.updateInterval = 1.0f/30.0f;
	accel.delegate = self;
}

// This method is called each frame to update the scene.
// It is called from the display link every time the screen wants to redraw itself.
- (void)update {
	// Step (simulate) the space based on the time since the last update.
	cpFloat dt = displayLink.duration*displayLink.frameInterval;
	[space step:dt];
	
	// Update the button.
	// This sets the position and rotation of the button to match the rigid body.
	[fallingButton updatePosition];
}

- (void)accelerometer:(UIAccelerometer *)accelerometer didAccelerate:(UIAcceleration *)accel {
	// Setting the gravity based on the tilt of the device is easy.
	space.gravity = cpvmult(cpv(accel.x, -accel.y), 100.0f);
}

// The view disappeared. Stop the animation timers and tilt callbacks.
- (void)viewDidDisappear:(BOOL)animated {
	// Remove the timer.
	[displayLink invalidate];
	displayLink = nil;
	
	// Remove the accelerometer delegate.
	[UIAccelerometer sharedAccelerometer].delegate = nil;
}

- (void)dealloc {
	[fallingButton release];
	
	[super dealloc];
}

@end