CCScrollView for cocos2d-x that works? - cross-platform

Edit: With enough rewriting, and commenting, I have it running, will post final below for others.
Edit2: I've been more updating my own version, and I've learned the original coder was not very good with C++, you may want to go over everything in there before using it, check out comments below post
The currently available CCScrollView for cocos2d-x suffers from one major flaw: It's messed up.
Specifically, the cpp function headers do not match the .h file's.
The cpp file refers to UIEvent and NSMutableArray, not X-Platform.
The scroll view itself must have an array added, which limits your ability to use a for loop to create your menu items (if creating menu items), since you can't use a mutable one, and they don't subclass each other like iOS so you can't just use a mutable then say CCArray * array = mutable->getMutableArray() or similar.
Also, the defines are outdated (though fixable through adding the CCX_SAFE_DELETE via the translation rules on the cocos2d-x site), and a variety of random templates trying to create NS and UI objects that are not even within the project for I have no clue why.
The basic idea is, aside from the addition of using mutable arrays, this class is way too messed up to bother translating to C++. I don't believe the author compiled the version he has up, as the headers don't match. I thought I downloaded both (there's 2 of each) the cpp and .h files, and both have UIEvent instead of CC along with the other issues.
Note: I have fixed a variety of the above, and changed SelectorProtocol to CCObject in various places, but it's just getting overwhelming.
Perhaps I should use the Parallax node instead?
This is just ridiculous, perhaps it's great for Cocos2d but it's not working on c2d-X. Anyway, I'll be recoding and searching, thanks for any suggestions!
Edit: Here's the classes I'm now using. Not done editing, but these will get anyone started, and compiling on more than just iPhone
Header:
#ifndef __CCSCROLLLAYER__
#define __CCSCROLLLAYER__
// CCScrollLayer.h
//
// Copyright 2010 DK101
// http://dk101.net/2010/11/30/implementing-page-scrolling-in-cocos2d/
//
// Copyright 2010 Giv Parvaneh.
// http://www.givp.org/blog/2010/12/30/scrolling-menus-in-cocos2d/
//
// Copyright 2011 Stepan Generalov
//
// Copyright 2011 Eli Yukelzon
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
// Original source: https://github.com/cocos2d/cocos2d-iphone-extensions/tree/master/Extensions/CCScrollLayer
// Last updated: October 1, 2011
#include "cocos2d.h"
namespace cocos2d {
class CCScrollLayer;
class CCScrollLayerDelegate
{
public:
/** Called when scroll layer begins scrolling.
* Usefull to cancel CCTouchDispatcher standardDelegates.
*/
virtual void scrollLayerScrollingStarted(CCScrollLayer* sender) {}
/** Called at the end of moveToPage:
* Doesn't get called in selectPage:
*/
virtual void scrollLayerScrolledToPageNumber(CCScrollLayer* sender, unsigned int page) {}
};
/*
It is a very clean and elegant subclass of CCLayer that lets you pass-in an array
of layers and it will then create a smooth scroller.
Complete with the "snapping" effect. You can create screens with anything that can be added to a CCLayer.
*/
class CCScrollLayer : public CCLayer
{
int currentScreen; //added
int totalScreens;
float scrollWidth;
float scrollHeight;
float startWidth;
float startHeight;
int startSwipe;
public:
//CCScrollLayer();
~CCScrollLayer();
static CCScrollLayer* nodeWithLayers(CCArray* layers, int widthOffset);
bool initWithLayers(CCArray* layers, int widthOffset);
/** Updates all pages positions & adds them as children if needed.
* Can be used to update position of pages after screen reshape, or
* for update after dynamic page add/remove.
*/
void updatePages();
/** Adds new page and reorders pages trying to set given number for newly added page.
* If number > pages count - adds new page to the right end of the scroll layer.
* If number <= 0 - adds new page to the left end of the scroll layer.
* #attention Designated addPage method.
*/
void addPage(CCLayer* aPage, unsigned int pageNumber);
/** Adds new page to the right end of the scroll layer. */
void addPage(CCLayer* aPage);
/** Removes page if it's one of scroll layers pages (not children)
* Does nothing if page not found.
*/
void removePage(CCLayer* aPage);
/** Removes page with given number. Doesn nothing if there's no page for such number. */
void removePageWithNumber(unsigned int pageNumber);
/* Moves scrollLayer to page with given number & invokes delegate
* method scrollLayer:scrolledToPageNumber: at the end of CCMoveTo action.
* Does nothing if number >= totalScreens or < 0.
*/
void moveToPage(unsigned int pageNumber);
/* Immedeatly moves scrollLayer to page with given number without running CCMoveTo.
* Does nothing if number >= totalScreens or < 0.
*/
void selectPage(unsigned int pageNumber);
CC_SYNTHESIZE(CCScrollLayerDelegate*, m_pDelegate, Delegate);
/** Calibration property. Minimum moving touch length that is enough
* to cancel menu items and start scrolling a layer.
*/
CC_SYNTHESIZE(CGFloat, m_fMinimumTouchLengthToSlide, MinimumTouchLengthToSlide);
/** Calibration property. Minimum moving touch length that is enough to change
* the page, without snapping back to the previous selected page.
*/
CC_SYNTHESIZE(CGFloat, m_fMinimumTouchLengthToChangePage, MinimumTouchLengthToChangePage);
/** If YES - when starting scrolling CCScrollLayer will claim touches, that are
* already claimed by others targetedTouchDelegates by calling CCTouchDispatcher#touchesCancelled
* Usefull to have ability to scroll with touch above menus in pages.
* If NO - scrolling will start, but no touches will be cancelled.
* Default is YES.
*/
CC_SYNTHESIZE(bool, m_bStealTouches, StealTouches);
/** Whenever show or not white/grey dots under the scroll layer.
* If yes - dots will be rendered in parents transform (rendered after scroller visit).
*/
CC_SYNTHESIZE(bool, m_bShowPagesIndicator, ShowPagesIndicator);
/** Position of dots center in parent coordinates.
* (Default value is screenWidth/2, screenHeight/4)
*/
CC_SYNTHESIZE_PASS_BY_REF(CCPoint, m_tPagesIndicatorPosition, PagesIndicatorPosition);
/** Total pages available in scrollLayer. */
unsigned int getTotalScreens() const;
/** Current page number, that is shown. Belongs to the [0, totalScreen] interval. */
CC_SYNTHESIZE_READONLY(unsigned int, m_uCurrentScreen, CurrentScreen);
/** Offset, that can be used to let user see next/previous page. */
CC_SYNTHESIZE(CGFloat, m_fPagesWidthOffset, PagesWidthOffset);
/** Offset that can be used to let user see empty space over first or last page. */
CC_SYNTHESIZE(CGFloat, m_fMarginOffset, MarginOffset);
/** Array of pages CCLayer's */
CC_SYNTHESIZE_READONLY(CCArray*, m_pLayers, Pages);
protected:
// The x coord of initial point the user starts their swipe.
CGFloat m_fStartSwipe;
// Internal state of scrollLayer (scrolling or idle).
int m_iState;
bool m_bStealingTouchInProgress;
// Holds the touch that started the scroll
CCTouch* m_pScrollTouch;
//void visit();
//void moveToPageEnded();
unsigned int pageNumberForPosition(const CCPoint& position);
CCPoint positionForPageWithNumber(unsigned int pageNumber);
void claimTouch(CCTouch* pTouch);
void cancelAndStoleTouch(CCTouch* pTouch, CCEvent* pEvent);
//void registerWithTouchDispatcher();
bool ccTouchBegan(CCTouch* pTouch, CCEvent* pEvent);
void ccTouchMoved(CCTouch* pTouch, CCEvent* pEvent);
void ccTouchEnded(CCTouch* pTouch, CCEvent* pEvent);
//void ccTouchCancelled(CCTouch* pTouch, CCEvent* pEvent);
};
} //end namespace
#endif
CPP
// CCScrollLayer.cpp
// Museum
//
// Created by GParvaneh on 29/12/2010.
// Copyright 2010. All rights reserved.
// Ported to C++ by Lior Tamam on 03/04/2011
#include "CCScrollLayer.h"
using namespace cocos2d;
CCScrollLayer* CCScrollLayer::nodeWithLayers(CCArray* layers, int widthOffset)
{
CCScrollLayer *pRet = new CCScrollLayer();
if (pRet && pRet->initWithLayers(layers, widthOffset))
{
pRet->autorelease();
return pRet;
}
CCX_SAFE_DELETE(pRet);
return NULL;
}
bool CCScrollLayer::initWithLayers(CCArray* layers, int widthOffset)
{
if (CCLayer::init())
{
// Make sure the layer accepts touches
CCTouchDispatcher::sharedDispatcher()->addTargetedDelegate(this,0,true);
// Set up the starting variables
//if(!widthOffset)
{
// widthOffset = 0;
}
currentScreen = 1;
// offset added to show preview of next/previous screens
scrollWidth = (int)CCDirector::sharedDirector()->getWinSize().width - widthOffset;
scrollHeight = (int)CCDirector::sharedDirector()->getWinSize().height;
startWidth = scrollWidth;
startHeight = scrollHeight;
// Loop through the array and add the screens
unsigned int i;
for (i=0; i<layers->count(); i++)
{
CCLayer* l = (CCLayer*)layers->objectAtIndex(i);
//l->setAnchorPoint(ccp(0,0));
//l->setPosition(ccp((i*scrollWidth),0));
addChild(l);
}
// Setup a count of the available screens
totalScreens = layers->count();
return true;
}
else
{
return false;
}
}
void CCScrollLayer::setMaximumScrollHeight(float maxHeight)
{
//Make the offset match expected pixels (include the current screen if at ccp(0,0)
maxHeight -= CCDirector::sharedDirector()->getWinSize().height;
maximumScrollHeight = maxHeight;
}
CCScrollLayer::~CCScrollLayer()
{
CCTouchDispatcher::sharedDispatcher()->removeDelegate(this);
CCLayer::onExit();
}
bool CCScrollLayer::ccTouchBegan(CCTouch *touch, CCEvent *withEvent)
{
//
// CCPoint touchPoint = touch->locationInView();
// touchPoint = CCDirector::sharedDirector()->convertToGL(touchPoint);
//
// startSwipe = (int)touchPoint.y;
return true;
}
void CCScrollLayer::ccTouchMoved(CCTouch *touch, CCEvent *withEvent)
{
CCPoint touchPoint = touch->locationInView();
CCPoint prevPoint = touch->previousLocationInView();
touchPoint = CCDirector::sharedDirector()->convertToGL(touchPoint);
prevPoint = CCDirector::sharedDirector()->convertToGL(prevPoint);
CCPoint difference = ccp( touchPoint.x - prevPoint.x , touchPoint.y - prevPoint.y);
CCPoint currentPos = this->getPosition();
currentPos = ccp( currentPos.x, currentPos.y+difference.y);
if (currentPos.y > maximumScrollHeight)
{
currentPos.y = maximumScrollHeight;
//this->setPositionY(maximumScrollHeight);
}
else if (currentPos.y < 0)
{
currentPos.y = 0;
// this->setPositionY(0);
}
this->setPosition(currentPos);
}
/*
void CCScrollLayer::ccTouchEnded(CCTouch *touch, CCEvent *withEvent)
{
//CCPoint touchPoint = touch->locationInView();
//touchPoint = CCDirector::sharedDirector()->convertToGL(touchPoint);
int newX = (int)touchPoint.x;
if ( (newX - startSwipe) < -scrollWidth / 3 && (currentScreen+1) <= totalScreens )
{
// this->moveToNextPage();
}
else if ( (newX - startSwipe) > scrollWidth / 3 && (currentScreen-1) > 0 )
{
// this->moveToPreviousPage();
}
else
{
// this->moveToPage(currentScreen);
}
}
*/

Reviving an old thread to add new information, for anyone who found this through a search engine: CCScrollView is now part of the GUI extension and seems to work fairly well. There should be no more need for the implementations referenced above.

Check out the forums on Cocos2d-x's site for the latest one. They're being reworked right now by the author based on this.
http://www.cocos2d-x.org/boards/18/topics/1090?r=10835#message-10835

Related

deathCounter not incrementing

Hi in this game that i am trying to modify there are 3 different types of enemies all with the same enemyHealth script. in this health script i am attempting to increase a counter every time an enemy dies. the code below shows the enemyHealth class
using UnityEngine;
namespace CompleteProject
{
public class EnemyHealth : MonoBehaviour
{
public int startingHealth = 100; // The amount of health the enemy starts the game with.
public bool isDead; // Whether the enemy is dead.
public int deathCount;
public int currentHealth; // The current health the enemy has.
public float sinkSpeed = 2.5f; // The speed at which the enemy sinks through the floor when dead.
public int scoreValue = 10; // The amount added to the player's score when the enemy dies.
public AudioClip deathClip; // The sound to play when the enemy dies.
Animator anim; // Reference to the animator.
AudioSource enemyAudio; // Reference to the audio source.
ParticleSystem hitParticles; // Reference to the particle system that plays when the enemy is damaged.
CapsuleCollider capsuleCollider; // Reference to the capsule collider.
bool isSinking; // Whether the enemy has started sinking through the floor.
void Awake ()
{
// Setting up the references.
anim = GetComponent <Animator> ();
enemyAudio = GetComponent <AudioSource> ();
hitParticles = GetComponentInChildren <ParticleSystem> ();
capsuleCollider = GetComponent <CapsuleCollider> ();
// Setting the current health when the enemy first spawns.
currentHealth = startingHealth;
}
void Update ()
{
// If the enemy should be sinking...
if(isSinking)
{
// ... move the enemy down by the sinkSpeed per second.
transform.Translate (-Vector3.up * sinkSpeed * Time.deltaTime);
}
}
public void TakeDamage (int amount, Vector3 hitPoint)
{
// If the enemy is dead...
if(isDead)
// ... no need to take damage so exit the function.
return;
// Play the hurt sound effect.
enemyAudio.Play ();
// Reduce the current health by the amount of damage sustained.
currentHealth -= amount;
// Set the position of the particle system to where the hit was sustained.
hitParticles.transform.position = hitPoint;
// And play the particles.
hitParticles.Play();
// If the current health is less than or equal to zero...
if(currentHealth <= 0)
{
// ... the enemy is dead.
Death ();
}
}
void Death ()
{
// The enemy is dead.
isDead = true;
// Turn the collider into a trigger so shots can pass through it.
capsuleCollider.isTrigger = true;
deathCount++;
Debug.Log("Deaths" + deathCount);
// Tell the animator that the enemy is dead.
anim.SetTrigger ("Dead");
// Change the audio clip of the audio source to the death clip and play it (this will stop the hurt clip playing).
enemyAudio.clip = deathClip;
enemyAudio.Play ();
}
public void StartSinking ()
{
// Find and disable the Nav Mesh Agent.
GetComponent <UnityEngine.AI.NavMeshAgent> ().enabled = false;
// Find the rigidbody component and make it kinematic (since we use Translate to sink the enemy).
GetComponent <Rigidbody> ().isKinematic = true;
// The enemy should no sink.
isSinking = true;
// Increase the score by the enemy's score value.
ScoreManager.score += scoreValue;
// After 2 seconds destory the enemy.
Destroy (gameObject, 2f);
}
}
}
all of the 3 enemies in the game have this script attached on their prefabs so they behave in the same way, so I cannot see why the counter will not increment when an enemy is destroyed.
when debugging the counter seems to go to 1 and then every kill after that just stays at 1 again.
that is correct. It's a member, so it will existing for each instance of your enemy. You want to track three different healths, right?
So enemy 1 start with 100, enemy 2 with 100, enemy 3 with 100. When enemy 1 gets 20 damage, it goes to 80 and 2,3 stay at 100, right? Why would the counter behave differently then? They all start at 0 and go to 1 when they die.
Either have an upper object (e.g. enemymanager) and have that count the times an enemy die (preferred solution) or use a static variable.

Render glitch with custom block boundaries minecraft

I'm creating a mod for Minecraft. Recently, I've tried to make a custom block, and I'm having two issues with it.
My main issue is that the block is rendering incorrectly. I want the block to be smaller in size than a full block. I successfully changed the block boundaries with setBlockBounds(), and while that did make the block render smaller and use the smaller boundaries, it causes other rendering issues. When I place the block, the floor below is becomes invisible and I can see through it, either to caves below, blocks behind it, or the void if there is nothing there. How do I fix that block not rendering? Here's a screenshot:
Additionally, my goal for this block is to emit an "aura" that gives players around it speed or some other potion effect. I have the basic code for finding players around the block and giving them speed, but I can't find a way to activate this method every tick or every X amount of ticks to ensure that it gives players within the box speed in a reliable manner. There are already some blocks in the normal game that do this, so it must be possible. How can I do this?
For your first issue, you need to override isOpaqueCube to return false. You'll also want to override isFullCube for other parts of the code, but that isn't as important for rendering. Example:
public class YourBlock {
// ... existing code ...
/**
* Used to determine ambient occlusion and culling when rebuilding chunks for render
*/
#Override
public boolean isOpaqueCube(IBlockState state) {
return false;
}
#Override
public boolean isFullCube(IBlockState state) {
return false;
}
}
Here's some info on rendering that mentions this.
Regarding your second problem, that's more complicated. It's generally achieved via a tile entity, though you can also use block updates (which is much slower). Good examples of this are BlockBeacon and TileEntityBeacon (for using tile entities) and BlockFrostedIce (for block updates). Here's some (potentially out of date) info on tile entities.
Here's an (untested) example of getting an update each tick this with tile entities:
public class YourBlock {
// ... existing code ...
/**
* Returns a new instance of a block's tile entity class. Called on placing the block.
*/
#Override
public TileEntity createNewTileEntity(World worldIn, int meta) {
return new TileEntityYourBlock();
}
}
/**
* Tile entity for your block.
*
* Tile entities normally store data, but they can also receive an update each
* tick, but to do so they must implement ITickable. So, don't forget the
* "implements ITickable".
*/
public class TileEntityYourBlock extends TileEntity implements ITickable {
#Override
public void update() {
// Your code to give potion effects to nearby players would go here
// If you only want to do it every so often, you can check like this:
if (this.worldObj.getTotalWorldTime() % 80 == 0) {
// Only runs every 80 ticks (4 seconds)
}
}
// The following code isn't required to make a tile entity that gets ticked,
// but you'll want it if you want (EG) to be able to set the effect.
/**
* Example potion effect.
* May be null.
*/
private Potion effect;
public void setEffect(Potion potionEffect) {
this.effect = potionEffect;
}
public Potion getEffect() {
return this.effect;
}
#Override
public void readFromNBT(NBTTagCompound compound) {
super.readFromNBT(compound);
int effectID = compound.getInteger("Effect")
this.effect = Potion.getPotionById(effectID);
}
public void writeToNBT(NBTTagCompound compound) {
super.writeToNBT(compound);
int effectID = Potion.getIdFromPotion(this.effect);
compound.setInteger("Effect", effectID);
}
}
// This line needs to go in the main registration.
// The ID can be anything so long as it isn't used by another mod.
GameRegistry.registerTileEntity(TileEntityYourBlock.class, "YourBlock");

How to write a box2d contact listener using cocos2d?

I've been reading various tutorials on how to write a contact listener, and I can't wrap my head around it.
Here is what I have so far:
In each of the classes that I have representing a physics object I do:
_body->SetUserData(self);
I write a contact listener class containing the following two methods:
void ContactListener::BeginContact(b2Contact* contact)
{
// Box2d objects that collided
b2Fixture* fixtureA = contact->GetFixtureA();
b2Fixture* fixtureB = contact->GetFixtureB();
// Sprites that collided
MyNode* actorA = (MyNode*) fixtureA->GetBody()->GetUserData();
MyNode* actorB = (MyNode*) fixtureB->GetBody()->GetUserData();
}
void ContactListener::EndContact(b2Contact* contact)
{
// Box2d objects that collided
b2Fixture* fixtureA = contact->GetFixtureA();
b2Fixture* fixtureB = contact->GetFixtureB();
// Sprites that collided
MyNode* actorA = (MyNode*) fixtureA->GetBody()->GetUserData();
MyNode* actorB = (MyNode*) fixtureB->GetBody()->GetUserData();
}
I don't know what to do next. I now have the two sprites which are colliding, but I want to do the following:
1) When they collide, I want to remove one of the sprites from the world, based on the type of object. (for example if one a cat object and the other is a mouse object, I want to remove the mouse object.
2) I want to let the cat object know it ate a mouse
3) I want the cat to continue moving as if it didn't contact with the mouse.
4) I still wan't the cat to collide normally with things like the terrain.
What do I do next ? I'm pretty clueless on what to do? How do I get the cat to continue to collide normally with the terrain, but not with the mouse? When do I remove the mouse?
Having an "Entity" class that hold the reference to the Box2D body and does the manipulations on it is definitely a good way to go. If you have a Spaceship class vs. a Meteor class, each of them can supply their own derived methods of controlling the body (AI), but each of them has common logic and code to support operations on "Things that have a body" (e.g. common "Entity" base class). I think you are on the right track.
It gets a little murky when the contacts start happening. This is where you start getting into the architecture of your overall system, not just the structure of the physics world or a single Coco2d Scene.
Here is how I have done this in the past:
First I set up the contact listener, listed below:
class EntityContactListener : public ContactListener
{
private:
GameWorld* _gameWorld;
EntityContactListener() {}
typedef struct
{
Entity* entA;
Entity* entB;
} CONTACT_PAIR_T;
vector<CONTACT_PAIR_T> _contactPairs;
public:
virtual ~EntityContactListener() {}
EntityContactListener(GameWorld* gameWorld) :
_gameWorld(gameWorld)
{
_contactPairs.reserve(128);
}
void NotifyCollisions()
{
Message* msg;
MessageManager& mm = GameManager::Instance().GetMessageMgr();
for(uint32 idx = 0; idx < _contactPairs.size(); idx++)
{
Entity* entA = _contactPairs[idx].entA;
Entity* entB = _contactPairs[idx].entB;
//DebugLogCPP("Contact Notification %s<->%s",entA->ToString().c_str(),entB->ToString().c_str());
msg = mm.CreateMessage();
msg->Init(entA->GetID(), entB->GetID(), Message::MESSAGE_COLLISION);
mm.EnqueueMessge(msg, 0);
msg = mm.CreateMessage();
msg->Init(entB->GetID(), entA->GetID(), Message::MESSAGE_COLLISION);
mm.EnqueueMessge(msg, 0);
}
_contactPairs.clear();
}
void PreSolve(b2Contact* contact, const b2Manifold* oldManifold)
{
b2Fixture* fixtureA = contact->GetFixtureA();
b2Body* bodyA = fixtureA->GetBody();
Entity* entityA = bodyA->GetUserData();
b2Fixture* fixtureB = contact->GetFixtureB();
b2Body* bodyB = fixtureB->GetBody();
Entity* entityB = bodyB->GetUserData();
if(test if entityA and entityB should not have collision response)
{
contact->SetEnabled(false);
}
// Do this if you want there to be collision notification, even if
// there is no response.
AddContactPair(entA,entB);
}
void AddContactPair(Entity* entA, Entity* entB)
{
for(uint32 idx = 0; idx < _contactPairs.size(); idx++)
{
if(_contactPairs[idx].entA == entA && _contactPairs[idx].entB == entB)
return;
// Not sure if this is needed...
if(_contactPairs[idx].entA == entB && _contactPairs[idx].entA == entB)
return;
}
CONTACT_PAIR_T pair;
pair.entA = entA;
pair.entB = entB;
_contactPairs.push_back(pair);
}
// BEWARE: You may get multiple calls for the same event.
void BeginContact(b2Contact* contact)
{
Entity* entA = (Entity*)contact->GetFixtureA()->GetBody()->GetUserData();
Entity* entB = (Entity*)contact->GetFixtureB()->GetBody()->GetUserData();
assert(entA != NULL);
assert(entB != NULL);
// Not sure this is still needed if you add it in the pre-solve.
// May not be necessary...
AddContactPair(entA, entB);
}
// BEWARE: You may get multiple calls for the same event.
void EndContact(b2Contact* contact)
{
}
};
Because of the way the engine works, you can get multiple contact hits for the same bodies. This listener filters them so if two Entities collide, you only get one message.
The Listener only stores the collisions that have occurred. It could be modified to further separate them into "begins" and "ends" for other purposes. Here, contact meant "you've been hit by something". I didn't need to know if it stopped contacting.
The call to NotifyCollisions is the "secret sauce". It sends out a message to both the contacted entities (via a message system) that they hit something and the other Entity is what they hit. Bullet hit ship. Bullet destroys self. Ship damages self based on bullet properties (GetDamageInflicted() method). This in turn signals the graphics system to remove the bullet from the display. If the ship was destroyed, it is destroyed as well.
From an overall execution standpoint:
Before you begin, assign the contact listener.
Each Cycle of your game:
Call "Update" on all your Entities. This updates their physics forces, etc.
Update Box2d world.
Call NotifyCollisions on Listener.
Remove dead Entities from system.
Was this helpful?

Creating Touchjoints/Mousejoints on small objects in box2d

I'm using Cocos2d iPhone with Box2D to create a basic physics engine.
Occasionally the user is required to drag around a small box2D object.
Creation of touchjoints on small objects is a bit hit and miss, with the game engine seeing it as a tap on blank space as often as actually creating the appropriate touchjoint. In practice this means the user is constantly mashing their fingers against the screen in vain attempts to move a stubborn object. I want the game to select small objects easily without this 'hit and miss' effect.
I could create the small objects with larger sensors around them, but this is not ideal because objects above a certain size (around 40px diameter) don't need this extra layer of complexity; and the small objects are simply the big objects scaled down to size.
What are some strategies I could use to allow the user experience to be better when moving small objects?
Here's the AABB code in ccTouchBegan:
b2Vec2 locationWorld = b2Vec2(touchLocation.x/PTM_RATIO, touchLocation.y/PTM_RATIO);
b2AABB aabb;
b2Vec2 delta = b2Vec2(1.0/PTM_RATIO, 1.0/PTM_RATIO);
//Changing the 1.0 here to a larger value doesn't make any noticeable difference.
aabb.lowerBound = locationWorld - delta;
aabb.upperBound = locationWorld + delta;
SimpleQueryCallback callback(locationWorld);
world->QueryAABB(&callback, aabb);
if(callback.fixtureFound){
//dragging code, updating sprite location etc.
}
SimpleQueryCallback code:
class SimpleQueryCallback : public b2QueryCallback
{
public:
b2Vec2 pointToTest;
b2Fixture * fixtureFound;
SimpleQueryCallback(const b2Vec2& point) {
pointToTest = point;
fixtureFound = NULL;
}
bool ReportFixture(b2Fixture* fixture) {
b2Body* body = fixture->GetBody();
if (body->GetType() == b2_dynamicBody) {
if (fixture->TestPoint(pointToTest)) {
fixtureFound = fixture;
return false;
}
}
return true;
}
};
What about a minimum collision box for touches? Objects with less than 40px diameter use the 40px diameter, all larger objects use their actual diameter.
What I ended up doing - thanks to iforce2d, was change ReportFixture in SimpileQueryCallback to:
bool ReportFixture(b2Fixture* fixture) {
b2Body* body = fixture->GetBody();
if (body->GetType() == b2_dynamicBody) {
//if (fixture->TestPoint(pointToTest)) {
fixtureFound = fixture;
return true;
//}
}
return true;
}
And increase the delta to 10.0/PTM_RATIO.

Repair Fragments in CGPathes of Cocoa Touch / Core Graphics

I have an unusual problem that caused my mind to stuck. I have created a drawing application that uses CGPathes to be created by a pen tool on the fly via the following view controller code.
#implementation WWLGrabManager
- (void) touchesBegan:(NSSet*)touchesIgnore withEvent:(UIEvent*)event {
currentPath = CGPathCreateMutable();
CGPathMoveToPoint(currentPath, NULL, pt.x, pt.y);
}
- (void) touchesMoved:(NSSet*)touchesIgnore withEvent:(UIEvent*)event {
CGPathAddLineToPoint(currentPath, NULL, pt.x, pt.y);
}
- (void) touchesEnded:(NSSet*)touches withEvent:(UIEvent*)event {
currentPath = [self newSmoothedPathWithPath:currentPath];
}
#end
After the drawing I iterate once again over the CGPath in order to smooth the drawn edges of the path. This shall make the path to not look so ugly by building the middle of each three points and applying a curve to each point. The code for smoothing edges mainly consists of the following pseudo code:
-(CGMutablePathRef)newSmoothedPathWithPath:(CGPathRef)path {
// This is pseudo code
CGMutablePathRef newPath = CGPathCreateMutable();
foreach (CGPoint point in path)
CGPathAddCurveToPoint(newPath, NULL,
ctrlPt1.x,ctrlPt1.y,ctrlPt2.x,ctrlPt2.y,
((*preLastPoint).x + (*lastPoint).x + (point).x)/3,
((*preLastPoint).y + (*lastPoint).y + (point).y)/3);
}
return newPath;
}
Now, after applying the smoothing function, the new CGPath causes fragments like in the picture here. I have double checked the control points, but I cannot figure out why this is happening.
For debugging reasons I have printed out a log of the CGPathes points and control points below.
MainPoint // ControlPoint1 // ControlPoint2
66.00, 91.00 // 67.00,87.00 // 64.25,95.75
59.00,110.00 // 60.75,105.25 // 58.00,113.75
55.00,125.00 // 56.00,121.25 // 54.00,128.75
51.00,140.00 // 52.00,136.25 // 50.25,144.25
48.00,157.00 // 48.75,152.75 // 47.25,161.00
45.00,173.00 // 45.75,169.00 // 44.75,176.75
44.00,188.00 // 44.25,184.25 // 43.75,191.75
43.00,203.00 // 43.25,199.25 // 43.00,207.00
43.00,219.00 // 43.00,215.00 // 43.00,223.00
43.00,235.00 // 43.00,231.00 // 43.00,239.25
43.00,252.00 // 43.00,247.75 // 43.00,256.00
43.00,268.00 // 43.00,264.00 // 44.25,272.00
48.00,284.00 // 46.75,280.00 // 49.50,287.50
54.00,298.00 // 52.50,294.50 // 56.75,300.75
65.00,309.00 // 62.25,306.25 // 68.75,310.75
80.00,316.00 // 76.25,314.25 // 84.00,316.50
96.00,318.00 // 92.00,317.50 // 101.50,318.25
118.00,319.00 // 112.50,318.75 // 124.75,319.00
145.00,319.00 // 138.25,319.00 // 151.25,319.00
170.00,319.00 // 163.75,319.00 // 175.50,318.25
192.00,316.00 // 186.50,316.75 // 199.50,314.00
222.00,308.00 // 214.50,310.00 // 226.75,306.75
241.00,303.00 // 236.25,304.25 // 245.00,301.75
257.00,298.00 // 253.00,299.25 // 260.50,295.75
271.00,289.00 // 267.50,291.25 // 273.25,285.25
280.00,274.00 // 277.75,277.75 // 280.50,270.25
282.00,259.00 // 281.50,262.75 // 282.00,254.50
282.00,241.00 // 282.00,245.50 // 280.50,237.25
276.00,226.00 // 277.50,229.75 // 273.50,222.75
266.00,213.00 // 268.50,216.25 // 263.00,208.75
254.00,196.00 // 257.00,200.25 // 249.75,192.50
237.00,182.00 // 241.25,185.50 // 234.00,179.75
225.00,173.00 // 228.00,175.25 // 221.75,170.50
212.00,163.00 // 215.25,165.50 // 208.50,160.25
198.00,152.00 // 201.50,154.75 // 194.00,148.75
182.00,139.00 // 186.00,142.25 // 178.00,136.75
166.00,130.00 // 170.00,132.25 // 162.25,128.50
Update: I used the algorithm described in this link, which was translated to Objective-C as follows:
CGPoint ctrl2 = controlPointForPoints(*lastPoint,*preLastPoint,currentPoint);
CGPoint ctrl1 = controlPointForPoints(*lastPoint,currentPoint,*preLastPoint);
static CGPoint controlPointForPoints(CGPoint pt, CGPoint pre, CGPoint post) {
CGPoint ctrlPt = CGPointMake(
middleOfPoints(middleOfPoints(pt.x, pre.x), middleOfPoints(symmetryOfPoints(pt.x, post.x), pt.x)),
middleOfPoints(middleOfPoints(pt.y, pre.y), middleOfPoints(symmetryOfPoints(pt.y, post.y), pt.y))
);
return ctrlPt;
}
static float symmetryOfPoints(float a,float b) {
return a - ((b-a)*smoothingFactor) / 100.0;
}
static float middleOfPoints(float a, float b) {
return (a+b) / 2.0;
}
However, exchanging the two control points does not lead to satisfactory results but increases the number of fragments enourmously. I'd appreciate any further help.
Unfortunately, your code doesn't show how the control points of the smoothed path are calculated. But they are the problem. Most of them are on the wrong side of the start or end point of a curve segment. As a consequence, your path makes a very tight loop or S curve at many main points.
Another indication of this is that while the path generally turns left, many path segments are to the right.
I don't quite understand why you have these white half circles. It seems the even-odd rule is used to draw the path instead of the non-zero winding rule, which probably cannot be changed. Anyway, the problem will disappear if you fix the above problem.