Objective C Pointers problem with cocos2d - objective-c

What im trying to do is to initialize a b2Body dynamically for any object that i have. Here is the code:
b2Body * mallet1;
[self setMalletProperties: &blockBodyDef : mallet1];
-(void) setMalletProperties:(b2BodyDef *)body :(b2Body *) mallet {
body->type = b2_dynamicBody;
mallet = world->CreateBody(body);
b2Vec2 newLocation = b2Vec2([self getScreenSize].width/2/PTM_RATIO,[self getScreenSize].height * .2833/PTM_RATIO);
//b2Vec2 newLocation = b2Vec2(x, y);
mallet->SetTransform(newLocation,0.0f);
mallet->SetFixedRotation(true);
b2FixtureDef fixtureDef;
b2CircleShape dynamicCircle;
dynamicCircle.m_radius = 0.85;
fixtureDef.shape = &dynamicCircle;
fixtureDef.restitution =0.1f;
fixtureDef.density = 1.0f;
fixtureDef.friction = 0.0f;
mallet1Fixture = mallet->CreateFixture(&fixtureDef);
if ([self getScreenSize].width == 768) {
dynamicCircle.m_radius = 1.75;
}
}
Its not working properly, cause its not putting the body that i created into the variable mallet1. What am i doing wrong?
Thank you

Try this:
-(void) setMalletProperties:(b2BodyDef *)body :(b2Body **) mallet {
body->type = b2_dynamicBody;
*mallet = world->CreateBody(body);
...
}
Your current code doesn't provide a way to access the same reference to mallet again.
Alternatively you could change the method signature like so:
-(b2Body*) setMalletProperties:(b2BodyDef *)body and include a return mallet; at the end

Related

Box2d: Elastic rope takes time to get back to its initial position

I am creating a rubber band in Box2d. Here is my code.
// init physics
[self initPhysics];
// Create ball body
CCSprite *ball = [CCSprite spriteWithFile:#"rubberband.png"];
ball.position = ccp(100, 100);
ball.tag = 1;
// [self addChild:ball];
//=======Params
// Position and size
b2Vec2 lastPos = b2Vec2(154.0/PTM_RATIO,65.0/PTM_RATIO); //set position first body
float widthBody = 2.0/PTM_RATIO;
float heightBody = 2.0/PTM_RATIO;
// Body params
float density = 0.0;
float restitution = 0.5;
float friction = 0.5;
// Distance joint
float dampingRatio = 0.85;
float frequencyHz = 10;
// Rope joint
float kMaxWidth = 50.0/PTM_RATIO;
// Bodies
int countBodyInChain = 68;
b2Body* prevBody;
//========Create bodies and joints
for (int k = 0; k < countBodyInChain; k++) {
b2BodyDef bodyDef;
if(k==0 || k==countBodyInChain-1) bodyDef.type = b2_staticBody; //first and last bodies are static
else bodyDef.type = b2_dynamicBody;
bodyDef.position = lastPos;
bodyDef.fixedRotation = YES;
b2Body* body = world->CreateBody(&bodyDef);
b2PolygonShape distBodyBox;
distBodyBox.SetAsBox(widthBody, heightBody);
b2FixtureDef fixDef;
fixDef.density = density;
fixDef.restitution = restitution;
fixDef.friction = friction;
fixDef.shape = &distBodyBox;
body->CreateFixture(&fixDef);
if(k>0) {
b2RevoluteJointDef armJointDef;
armJointDef.Initialize(prevBody, body, lastPos);
armJointDef.enableMotor = true;
armJointDef.enableLimit = true;
armJointDef.maxMotorTorque = 1;
world->CreateJoint(&armJointDef);
//Create rope joint
b2RopeJointDef rDef;
rDef.maxLength = (body->GetPosition() - prevBody->GetPosition()).Length() * kMaxWidth;
rDef.localAnchorA = rDef.localAnchorB = b2Vec2_zero;
rDef.bodyA = prevBody;
rDef.bodyB = body;
rDef.collideConnected = false;
world->CreateJoint(&rDef);
} //if k>0
lastPos += b2Vec2(widthBody, 0); //modify b2Vect for next body
prevBody = body;
} //for -loop
[self scheduleUpdate];
}
return self;
}
Problem is that when the app starts, rubber band appears in stretched form in U shape and then it gradually start contracting and coming to become straight horizontally. Can anyone please tell me why it is happening? I want the rubber band to be without being stretched in the beginning.
Best Regards
You don't update lastPos so all bodies occupy the same position initially. Box2D will force them apart and this could lead to the problem.

extracting variable from one method to another method in the same .m file in objective c, xcode

Hello I would like to know how to use a variable from this method
+ (NSString *) yourCalculation:(NSString *)height:(NSString *)weight{
double bmiVal = 0;
if (weight > 0 && height > 0) {
CGFloat wInPounds = [weight floatValue];
CGFloat hInInches = [height floatValue];
CGFloat hInCms = hInInches *0.393700787;
}
}
in this method
+(NSString *) actualCalculation{
float val = wInPounds/(hInCms*hInCms);
float bmiVal = val *703;
}
This is only a small segment of the code, but it gets across what I want to do with it.
If anyone can tell me how to do this, I would appreciate it.
Thanking You
Create a custom class that has properties for the various values you want to share and return an instance of that. For example, assuming a MyNumerics class with the obvious properties:
+ (MyNumerics *) yourCalculation:(NSString *)height weight:(NSString *)weight {
MyNumerics *result = nil;
double bmiVal = 0;
if (weight > 0 && height > 0) {
result = [[MyNumerics alloc] init];
result.wInPounds = [weight floatValue];
result.hInInches = [height floatValue];
result.hInCms = hInInches *0.393700787;
}
return result;
}
Have the calling routine use the result's properties in its calculations.

How to get video frame of the AVPlayer?

I have PlayerView class for displaying AVPlayer's playback. Code from documentation.
#import <UIKit/UIKit.h>
#import <AVFoundation/AVFoundation.h>
#interface PlayerView : UIView
#property (nonatomic) AVPlayer *player;
#end
#implementation PlayerView
+ (Class)layerClass {
return [AVPlayerLayer class];
}
- (AVPlayer*)player {
return [(AVPlayerLayer *)[self layer] player];
}
- (void)setPlayer:(AVPlayer *)player {
[(AVPlayerLayer *)[self layer] setPlayer:player];
}
#end
I set up my AVPlayer (contains video asset with size 320x240) in this PlayerView (with frame.size.width = 100, frame.size.height = 100) and my video is resized. How can i get size of video after adding in PlayerView?
In iOS 7.0 added new feature:
AVPlayerLayer has property videoRect.
This worked for me. When you don't have the AVPLayerLayer.
- (CGRect)videoRect {
// #see http://stackoverflow.com/a/6565988/1545158
AVAssetTrack *track = [[self.player.currentItem.asset tracksWithMediaType:AVMediaTypeVideo] firstObject];
if (!track) {
return CGRectZero;
}
CGSize trackSize = [track naturalSize];
CGSize videoViewSize = self.videoView.bounds.size;
CGFloat trackRatio = trackSize.width / trackSize.height;
CGFloat videoViewRatio = videoViewSize.width / videoViewSize.height;
CGSize newSize;
if (videoViewRatio > trackRatio) {
newSize = CGSizeMake(trackSize.width * videoViewSize.height / trackSize.height, videoViewSize.height);
} else {
newSize = CGSizeMake(videoViewSize.width, trackSize.height * videoViewSize.width / trackSize.width);
}
CGFloat newX = (videoViewSize.width - newSize.width) / 2;
CGFloat newY = (videoViewSize.height - newSize.height) / 2;
return CGRectMake(newX, newY, newSize.width, newSize.height);
}
Found a solution:
Add to PlayerView class:
- (CGRect)videoContentFrame {
AVPlayerLayer *avLayer = (AVPlayerLayer *)[self layer];
// AVPlayerLayerContentLayer
CALayer *layer = (CALayer *)[[avLayer sublayers] objectAtIndex:0];
CGRect transformedBounds = CGRectApplyAffineTransform(layer.bounds, CATransform3DGetAffineTransform(layer.sublayerTransform));
return transformedBounds;
}
Here's the solution that's working for me, takes into account the positioning of the AVPlayer within the view. I just added this to the PlayerView custom class. I had to solve this because doesn't appear that videoRect is working in 10.7.
- (NSRect) videoRect {
NSRect theVideoRect = NSMakeRect(0,0,0,0);
NSRect theLayerRect = self.playerLayer.frame;
NSSize theNaturalSize = NSSizeFromCGSize([[[self.movie asset] tracksWithMediaType:AVMediaTypeVideo][0] naturalSize]);
float movieAspectRatio = theNaturalSize.width/theNaturalSize.height;
float viewAspectRatio = theLayerRect.size.width/theLayerRect.size.height;
if (viewAspectRatio < movieAspectRatio) {
theVideoRect.size.width = theLayerRect.size.width;
theVideoRect.size.height = theLayerRect.size.width/movieAspectRatio;
theVideoRect.origin.x = 0;
theVideoRect.origin.y = (theLayerRect.size.height/2) - (theVideoRect.size.height/2);
}
else if (viewAspectRatio > movieAspectRatio) {
theVideoRect.size.width = movieAspectRatio * theLayerRect.size.height;
theVideoRect.size.height = theLayerRect.size.height;
theVideoRect.origin.x = (theLayerRect.size.width/2) - (theVideoRect.size.width/2);
theVideoRect.origin.y = 0;
}
return theVideoRect;
}
Here is Eric Badros' answer ported to iOS. I also added preferredTransform handling. This assumes _player is an AVPlayer
- (CGRect) videoRect {
CGRect theVideoRect = CGRectZero;
// Replace this with whatever frame your AVPlayer is playing inside of:
CGRect theLayerRect = self.playerLayer.frame;
AVAssetTrack *track = [_player.currentItem.asset tracksWithMediaType:AVMediaTypeVideo][0];
CGSize theNaturalSize = [track naturalSize];
theNaturalSize = CGSizeApplyAffineTransform(theNaturalSize, track.preferredTransform);
theNaturalSize.width = fabs(theNaturalSize.width);
theNaturalSize.height = fabs(theNaturalSize.height);
CGFloat movieAspectRatio = theNaturalSize.width / theNaturalSize.height;
CGFloat viewAspectRatio = theLayerRect.size.width / theLayerRect.size.height;
// Note change this *greater than* to a *less than* if your video will play in aspect fit mode (as opposed to aspect fill mode)
if (viewAspectRatio > movieAspectRatio) {
theVideoRect.size.width = theLayerRect.size.width;
theVideoRect.size.height = theLayerRect.size.width / movieAspectRatio;
theVideoRect.origin.x = 0;
theVideoRect.origin.y = (theLayerRect.size.height - theVideoRect.size.height) / 2;
} else if (viewAspectRatio < movieAspectRatio) {
theVideoRect.size.width = movieAspectRatio * theLayerRect.size.height;
theVideoRect.size.height = theLayerRect.size.height;
theVideoRect.origin.x = (theLayerRect.size.width - theVideoRect.size.width) / 2;
theVideoRect.origin.y = 0;
}
return theVideoRect;
}
Here is a Swift solution based on Andrey Banshchikov's answer. His solution is especially useful when you don't have access to AVPLayerLayer.
func currentVideoFrameSize(playerView: AVPlayerView, player: AVPlayer) -> CGSize {
// See https://stackoverflow.com/a/40164496/1877617
let track = player.currentItem?.asset.tracks(withMediaType: .video).first
if let track = track {
let trackSize = track.naturalSize
let videoViewSize = playerView.bounds.size
let trackRatio = trackSize.width / trackSize.height
let videoViewRatio = videoViewSize.width / videoViewSize.height
var newSize: CGSize
if videoViewRatio > trackRatio {
newSize = CGSize(width: trackSize.width * videoViewSize.height / trackSize.height, height: videoViewSize.height)
} else {
newSize = CGSize(width: videoViewSize.width, height: trackSize.height * videoViewSize.width / trackSize.width)
}
return newSize
}
return CGSize.zero
}
Andrey's answer worked for file/local/downloaded video, but didn't work for streamed HLS video. As a 2nd attempt, I was able to find the video track right inside of the "tracks" property of currentItem. Also rewritten to Swift.
private func videoRect() -> CGRect {
// Based on https://stackoverflow.com/a/40164496 - originally objective-c
var trackTop: AVAssetTrack? = nil
if let track1 = self.player?.currentItem?.asset.tracks(withMediaType: AVMediaType.video).first {
trackTop = track1
}
else {
// For some reason the above way wouldn't find the "track" for streamed HLS video.
// This seems to work for streamed HLS.
if let tracks = self.player?.currentItem?.tracks {
for avplayeritemtrack in tracks {
if let assettrack = avplayeritemtrack.assetTrack {
if assettrack.mediaType == .video {
// Found an assetTrack here?
trackTop = assettrack
break
}
}
}
}
}
guard let track = trackTop else {
print("Failed getting track")
return CGRect.zero
}
let trackSize = track.naturalSize
let videoViewSize = self.view.bounds.size
let trackRatio = trackSize.width / trackSize.height
let videoViewRatio = videoViewSize.width / videoViewSize.height
let newSize: CGSize
if videoViewRatio > trackRatio {
newSize = CGSize.init(width: trackSize.width * videoViewSize.height / trackSize.height, height: videoViewSize.height)
}
else {
newSize = CGSize.init(width: videoViewSize.width, height: trackSize.height * videoViewSize.width / trackSize.width)
}
let newX: CGFloat = (videoViewSize.width - newSize.width) / 2
let newY: CGFloat = (videoViewSize.height - newSize.height) / 2
return CGRect.init(x: newX, y: newY, width: newSize.width, height: newSize.height)
}
Much simpler approach
I found another way. Seems to be much easier, if you're using AVPlayerViewController. Why didn't I find this earlier. 🀧
return self.videoBounds
2022 SwiftUI
struct PlayerViewController: UIViewControllerRepresentable {
private let avController = AVPlayerViewController()
var videoURL: URL?
private var player: AVPlayer {
return AVPlayer(url: videoURL!)
}
// πŸ‘‡πŸΌπŸ‘‡πŸΌπŸ‘‡πŸΌ HERE πŸ‘‡πŸΌπŸ‘‡πŸΌπŸ‘‡πŸΌ
func getVideoFrame() -> CGRect {
self.avController.videoBounds
}
// πŸ‘†πŸΌπŸ‘†πŸΌπŸ‘†πŸΌ HERE πŸ‘†πŸΌπŸ‘†πŸΌπŸ‘†πŸΌ
func makeUIViewController(context: Context) -> AVPlayerViewController {
avController.modalPresentationStyle = .fullScreen
avController.player = player
avController.player?.play()
return avController
}
func updateUIViewController(_ playerController: AVPlayerViewController, context: Context) {}
}

schedule update issue

This might sound pretty straightforward. I've created a method and I've called it as below in the init method.
[self createNewSpr:ccp(s.width * 0.25,s.height-200)];
[self createNewSpr:ccp(s.width * 0.50,s.height-200)];
[self createNewSpr:ccp(s.width * 0.75,s.height-200)];
[self scheduleUpdate];
I've defined a for loop in my update method that imposes a gravity higher than that of the world on the sprites. Only the last call is affected by the new gravity but the first and second act on the world gravity. I am not sure what is wrong but I suspect it to be the scheduleUpdate. Please Help.
Edit: Update Method :
-(void) update: (ccTime) dt
{
int32 velocityIterations = 8;
int32 positionIterations = 1;
world->Step(dt, velocityIterations, positionIterations);
for (b2Body* b = world->GetBodyList(); b; b = b->GetNext())
{
if (b == sprite)
{
b->ApplyForce( b2Vec2(0.0,20*b->GetMass()),b->GetWorldCenter());
}
}
}
the createNewSpr:
-(void) createNewSpr:(CGPoint)pos {
//CGSize s = [CCDirector sharedDirector].winSize;
b2Vec2 startPos = [self toMeters:pos];
CGFloat linkHeight = 0.24;
CGFloat linkWidth = 0.1;
b2BodyDef bodyDef;
bodyDef.type = b2_dynamicBody;
bodyDef.position = startPos;
b2FixtureDef fixtureDef;
fixtureDef.density = 0.1;
b2PolygonShape polygonShape;
polygonShape.SetAsBox(linkWidth,linkHeight);
fixtureDef.shape = &polygonShape;
//first
b2Body* link = world->CreateBody( &bodyDef );
link->CreateFixture( &fixtureDef );
PhysicsSprite* segmentSprite = [PhysicsSprite spriteWithFile:#"sg.png"];
[self addChild:segmentSprite];
[segmentSprite setPhysicsBody:link];
b2RevoluteJointDef revoluteJointDef;
revoluteJointDef.localAnchorA.Set( 0, linkHeight);
revoluteJointDef.localAnchorB.Set( 0, -linkHeight);
for (int i = 0; i < 10; i++) {
b2Body* newLink = world->CreateBody( &bodyDef );
newLink->CreateFixture( &fixtureDef );
PhysicsSprite* segmentSprite = [PhysicsSprite spriteWithFile:#"sg.png"];
[self addChild:segmentSprite];
[segmentSprite setPhysicsBody:link];
revoluteJointDef.bodyA = link;
revoluteJointDef.bodyB = newLink;
world->CreateJoint( &revoluteJointDef );
link = newLink;//next iteration
}
PhysicsSprite* circleBodySprite = [PhysicsSprite spriteWithFile:#"cb.png"];
[self addChild:circleBodySprite z:1];
b2CircleShape circleShape;
circleShape.m_radius = circleBodySprite.contentSize.width/2 / PTM_RATIO;
fixtureDef.shape = &circleShape;
b2Body* chainBase =world->CreateBody( &bodyDef );
chainBase->CreateFixture( &fixtureDef );
[circleBodySprite setPhysicsBody:chainBase];
sprite = chainBase;
revoluteJointDef.bodyA = link;
revoluteJointDef.bodyB = chainBase;
revoluteJointDef.localAnchorA.Set(0,linkWidth);
revoluteJointDef.localAnchorB.Set(0,linkWidth);
world->CreateJoint( &revoluteJointDef );
}
The problem is with createNewSpr method...
You have assigned sprite to a body... So When you call this method three times, sprite refers to 3rd object only..
When you compare in update method, it just puts gravity on 3rd object...
Hope this helps.. :)

cocos2d and chipmunk rotate object after collision

hi
im pretty new to both frameworks. but maybe someone can point me into the right direction:
basically i try to bounce a ball of a shape. (works fine)
but it would be great when the ball would rotate, too
here is my (copy & paste) code
// BallLayer.m
#import "BallLayer.h"
void updateShape(void* ptr, void* unused){
cpShape* shape = (cpShape*)ptr;
Sprite* sprite = shape->data;
if(sprite){
cpBody* body = shape->body;
[sprite setPosition:cpv(body->p.x, body->p.y)];
}
}
#implementation BallLayer
-(void)tick:(ccTime)dt{
cpSpaceStep(space, 1.0f/60.0f);
cpSpaceHashEach(space->activeShapes, &updateShape, nil);
}
-(void)setupChipmunk{
cpInitChipmunk();
space = cpSpaceNew();
space->gravity = cpv(0,-2000);
space->elasticIterations = 1;
[self schedule: #selector(tick:) interval: 1.0f/60.0f];
cpBody* ballBody = cpBodyNew(200.0, cpMomentForCircle(100.0, 10, 10, cpvzero));
ballBody->p = cpv(150, 400);
cpSpaceAddBody(space, ballBody);
cpShape* ballShape = cpCircleShapeNew(ballBody, 20.0, cpvzero);
ballShape->e = 0.8;
ballShape->u = 0.8;
ballShape->data = ballSprite;
ballShape->collision_type = 1;
cpSpaceAddShape(space, ballShape);
cpBody* floorBody = cpBodyNew(INFINITY, INFINITY);
floorBody->p = cpv(0, 0);
cpShape* floorShape = cpSegmentShapeNew(floorBody, cpv(0,0), cpv(320,160), 0);
floorShape->e = 0.5;
floorShape->u = 0.1;
floorShape->collision_type = 0;
cpSpaceAddStaticShape(space, floorShape);
floorShape = cpSegmentShapeNew(floorBody, cpv(0,200), cpv(320,0), 0);
cpSpaceAddStaticShape(space, floorShape);
}
-(id)init{
self = [super init];
if(nil != self){
ballSprite = [Sprite spriteWithFile:#"ball2.png"];
[ballSprite setPosition:CGPointMake(150, 400)];
[self add:ballSprite];
[self setupChipmunk];
}
return self;
}
#end
please help me out.
well when i decided psoting it i found the solution :)
void updateShape(void* ptr, void* unused)
{
cpShape* shape = (cpShape*)ptr;
Sprite* sprite = shape->data;
if(sprite){
cpBody* body = shape->body;
[sprite setPosition:cpv(body->p.x, body->p.y)];
[sprite setRotation: (float) CC_RADIANS_TO_DEGREES( -body->a )];
}
}