Minmax Algorithm for tic-tac-toe game in Objective-C - iphone

I am writing a minmax algorithm as the artificial intelligence for a tic-tac-toe game, I followed the similar instruction here, but the algorithm seems not intelligent enough, even though I tried to search deeper in the tree, can anyone help to analyze where goes wrong? thank you very much in advance!
- (int) miniMax:(int)depth : (UIImage*) player {
NSMutableArray *steps = [self generateMoves];
if (depth == 0 || [steps count] == 0) {
return [self evaluate];
}
int bestScore = player == myImg ? -1000000 : 1000000;
int currentScore = 0;
for (UIImageView *step in steps) {
step.image = player;
if (player == myImg) {
UIImage *opp = player == xImg ? oImg : xImg;
currentScore = [self miniMax:depth - 1 :opp];
if (currentScore > bestScore) {
bestScore = currentScore;
nextStep = step;
}
} else {
UIImage *opp = player == xImg ? oImg : xImg;
currentScore = [self miniMax:depth - 1 :opp];
if (currentScore < bestScore) {
bestScore = currentScore;
nextStep = step;
}
}
step.image = NULL;
}
return bestScore;
}
- (int) evaluate {
int score = 0;
score += [self evaluateLine:img0 :img1 :img2];
score += [self evaluateLine:img3 :img4 :img5];
score += [self evaluateLine:img6 :img7 :img8];
score += [self evaluateLine:img0 :img3 :img6];
score += [self evaluateLine:img1 :img4 :img7];
score += [self evaluateLine:img2 :img5 :img8];
score += [self evaluateLine:img2 :img4 :img6];
score += [self evaluateLine:img0 :img4 :img8];
return score;
}
- (int) evaluateLine:(UIImageView*)img1 :(UIImageView*)img2 :(UIImageView*)img3 {
int score = 0;
// first cell
if ([img1 image] == myImg) {
score = 1;
} else if ([img1 image] == oppImg){
score = -1;
}
// second cell
if ([img2 image] == myImg) {
if (score == 1) {
score = 10;
} else if (score == -1) {
return 0;
} else {
score = -1;
}
} else if ([img2 image] == oppImg){
if (score == -1) {
score = -10;
} else if (score == 1) {
return 0;
} else {
score = -1;
}
}
// third cell
if ([img3 image] == myImg) {
if (score > 0) {
score *= 10;
} else if (score < 0) {
return 0;
} else {
score = -1;
}
} else if ([img3 image] == oppImg){
if (score < 0) {
score *= 10;
} else if (score > 1) {
return 0;
} else {
score = -1;
}
}
return score;
}
What I use here is: if there exits the same image as the human player holds, the score plus 1. If there
are two or three player's image in a line or row or diagonal, the total score is 10 and 100 separately. If there exist both 'X' and 'O' in a same row, column or diagonal, the score is 0. Computer holds the negative score for these mentioned above.

Minimax assumes that the evaluation function is from the perspective of the first player. What you want is a function that always have a higher value if the first player is better.
It seems that your heuristic function adapts its value based on the current player (I am not an objective-C programmer). Change that function so that it does not know what myImg or oppImg is -- use xImg and oImg directly. Might just work.

Related

Switching textures of renderers in Unity3d

I am creating a character customization system. I have 2 shirts with their own 2 textures.
When I switch shirts the all of the textures are applying two both renderers instead of their own.
Any help would be appreciated. Here is my script.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class CharCustomizationOne : MonoBehaviour
{
public GameObject[] characters;
public GameObject[] maleHairs;
public GameObject[] maleShirts;
public GameObject[] malePants;
public GameObject[] femaleHairs;
public GameObject[] femaleShirts;
public GameObject[] femalePants;
public Texture[] charTexturesM;
public Texture[] charTexturesF;
public Texture[] shirtTextureM;
public Texture[] shirtTextureF;
public Texture[] pantTextureM;
public Texture[] pantTextureF;
private int currentCharacters;
private int currentMaleHair;
private int currentMaleShirts;
private int currentMalePants;
private int currentFemaleHair;
private int currentFemaleShirt;
private int currentFemalePant;
private int currentCharTextureM;
private int currentCharTextureF;
private int currentShirtTextureM;
private int currentShirtTextureF;
private int currentPantTextureM;
private int currentPantTextureF;
public Renderer rendererM;
public Renderer rendererF;
public Renderer[] rendererShirtM = new Renderer[2];
public Renderer[] rendererShirtF = new Renderer[2];
public Renderer[] rendererPantM = new Renderer[2];
public Renderer[] rendererPantF = new Renderer[2];
public void Update() {
//Gender Loop
for (int i = 0; i < characters.Length; i++)
{
if (i == currentCharacters)
{
characters[i].SetActive(true);
}
else
{
characters[i].SetActive(false);
}
}
//Hair Loop
if (currentCharacters == 0)
{
for (int i = 0; i < maleHairs.Length; i++)
{
if (i == currentMaleHair)
{
femaleHairs[i].SetActive(false);
maleHairs[i].SetActive(true);
}
else
{
femaleHairs[i].SetActive(false);
maleHairs[i].SetActive(false);
}
}
}
else
{
for (int i = 0; i < femaleHairs.Length; i++)
{
if (i == currentFemaleHair)
{
maleHairs[i].SetActive(false);
femaleHairs[i].SetActive(true);
}
else
{
maleHairs[i].SetActive(false);
femaleHairs[i].SetActive(false);
}
}
}
// Shirt Loop
if (currentCharacters == 0)
{
for (int i = 0; i < maleShirts.Length; i++)
{
if (i == currentMaleShirts)
{
femaleShirts[i].SetActive(false);
maleShirts[i].SetActive(true);
}
else
{
femaleShirts[i].SetActive(false);
maleShirts[i].SetActive(false);
}
}
}
else
{
for (int i = 0; i < femaleShirts.Length; i++)
{
if (i == currentFemaleShirt)
{
maleShirts[i].SetActive(false);
femaleShirts[i].SetActive(true);
}
else
{
maleShirts[i].SetActive(false);
femaleShirts[i].SetActive(false);
}
}
}
// Pants Loop
if (currentCharacters == 0)
{
for (int i = 0; i < malePants.Length; i++)
{
if (i == currentMalePants)
{
femalePants[i].SetActive(false);
malePants[i].SetActive(true);
}
else
{
femalePants[i].SetActive(false);
malePants[i].SetActive(false);
}
}
}
else
{
for (int i = 0; i < femalePants.Length; i++)
{
if (i == currentFemalePant)
{
malePants[i].SetActive(false);
femalePants[i].SetActive(true);
}
else
{
malePants[i].SetActive(false);
femalePants[i].SetActive(false);
}
}
}
//CharTexture Loop
if (currentCharacters == 0)
{
for (int i = 0; i < charTexturesM.Length; i++)
{
if (i == currentCharTextureM)
{
rendererM.sharedMaterial.mainTexture = charTexturesM[i];
}
}
}
else
{
for (int i = 0; i < charTexturesF.Length; i++)
{
if (i == currentCharTextureF)
{
rendererF.sharedMaterial.mainTexture = charTexturesF[i];
}
}
}
//Shirt Texture Loop
if (currentCharacters == 0)
{
for (int i = 0; i < maleShirts.Length; i++)
{
for (int j = 0; j < shirtTextureM.Length; j++)
{
if (j == currentShirtTextureM)
{
rendererShirtM[i].sharedMaterial.mainTexture = shirtTextureM[j];
}
}
}
}
else
{
for (int i = 0; i < femaleShirts.Length; i++)
{
for (int j = 0; j < shirtTextureF.Length; j++)
{
if (j == currentShirtTextureF)
{
rendererShirtF[i].sharedMaterial.mainTexture = shirtTextureF[j];
}
}
}
}
//Pant Texture Loop
if (currentCharacters == 0)
{
for (int i = 0; i < malePants.Length; i++)
{
for (int j = 0; j < pantTextureM.Length; j++)
{
if (j == currentPantTextureM)
{
rendererPantM[i].sharedMaterial.mainTexture = pantTextureM[j];
}
}
}
}
else
{
for (int i = 0; i < femalePants.Length; i++)
{
for (int j = 0; j < pantTextureF.Length; j++)
{
if (j == currentPantTextureF)
{
rendererPantF[i].sharedMaterial.mainTexture = pantTextureF[j];
}
}
}
}
}
//Switching Characters
public void SwitchCharacters() {
Debug.Log("Method Called!");
if (currentCharacters == characters.Length - 1)
{
currentCharacters = 0;
}
else
{
currentCharacters++;
}
}
//Switching Hairs
public void SwitchHairs() {
Debug.Log("MaleHairsCalled");
if (currentMaleHair == maleHairs.Length - 1)
{
currentMaleHair = 0;
}
else
{
currentMaleHair++;
}
if (currentFemaleHair == femaleHairs.Length - 1)
{
currentFemaleHair= 0;
}
else
{
currentFemaleHair++;
}
}
//Switching Shirts
public void SwitchShirts()
{
Debug.Log("MaleShirtCalled");
if (currentMaleShirts == maleShirts.Length - 1)
{
currentMaleShirts = 0;
}
else
{
currentMaleShirts++;
}
if (currentFemaleShirt == femaleShirts.Length - 1)
{
currentFemaleShirt = 0;
}
else
{
currentFemaleShirt++;
}
}
//Switching Pants
public void SwitchPants() {
Debug.Log("MalePantsCalled");
if (currentMalePants == malePants.Length - 1)
{
currentMalePants = 0;
}
else
{
currentMalePants++;
}
if (currentFemalePant == femalePants.Length - 1)
{
currentFemalePant = 0;
}
else
{
currentFemalePant++;
}
}
//Switching Character Textures
public void SwitchCharTexture()
{
Debug.Log("Char Texture Method Was Called!");
if (currentCharTextureM == charTexturesM.Length - 1)
{
currentCharTextureM = 0;
}
else
{
currentCharTextureM++;
}
if (currentCharTextureF == charTexturesF.Length - 1)
{
currentCharTextureF = 0;
}
else
{
currentCharTextureF++;
}
}
public void SwitchShirtTexture()
{
Debug.Log("Shirt Texture Method Was Called!");
if (currentShirtTextureM == shirtTextureM.Length - 1)
{
currentShirtTextureM = 0;
}
else
{
currentShirtTextureM++;
}
if (currentShirtTextureF == shirtTextureF.Length - 1)
{
currentShirtTextureF = 0;
}
else
{
currentShirtTextureF++;
}
}
public void SwitchPantTexture()
{
Debug.Log("Pant Texture Method Was Called!");
if (currentPantTextureM == pantTextureM.Length - 1)
{
currentPantTextureM = 0;
}
else
{
currentPantTextureM++;
}
if (currentPantTextureF == pantTextureF.Length - 1)
{
currentPantTextureF = 0;
}
else
{
currentPantTextureF++;
}
}
//End of Class
}
instead of Renderer.sharedMaterial
Modifying sharedMaterial will change the appearance of all objects using this material, and change material settings that are stored in the project too.
It is not recommended to modify materials returned by sharedMaterial. If you want to modify the material of a renderer use material instead.
rather use Renderer.material
Modifying material will change the material for this object only.
If the material is used by any other renderers, this will clone the shared material and start using it from now on.
Also check your loops .. some things seems odd there e.g.
for (int j = 0; j < someTextureArray.Length; j++)
{
if (j == currentTextureIndex)
{
someRenderer[i].material.mainTexture = someTextureArray[j];
}
}
seems just to be a very complex way of simply writing
someRenderer[i].material.mainTexture = someTextureArray[currentTextureIndex];
For all your switch methods: note that the easiest solution for wrap around a positive counter for indices in an array you can simply do
currentIndex = (currentIndex + 1) % accordingArray.Length;

Kinect for Windows V2

In my project I need to detect multiple gestures, I made a .gbd file with 2 continuous gestures. I can detect the continuous gesture when the .gbd file had one gesture. When there is 2 gestures I am unable to read any one gesture. Do any one know the reason? Here I post my code. I used Body basics example from sdk, and added necessary functions separately to avoid memory leakage.
//separate function to initialize database, vgb frame source and vgb frame reader
void CBodyBasics::InitializeVGB()
{
HRESULT hr;
std::wstring sDbFile = L"ContinuousGestures.gbd";
hr = CreateVisualGestureBuilderDatabaseInstanceFromFile(sDbFile.c_str(), &m_pGestureDatabase);
if (SUCCEEDED(hr))
{
OutputDebugString(L"Success DB\n");
}
else
{
OutputDebugString(L"Failure DB\n");
}
for (int i = 0; i < BODY_COUNT; i++)
{
// Source
hr = CreateVisualGestureBuilderFrameSource(m_pKinectSensor, 0, &m_pVGBFrameSource[i]);
if (SUCCEEDED(hr))
{
OutputDebugString(L"Source created\n");
}
else
{
OutputDebugString(L"Soure creation failed\n");
}
// Reader
hr = m_pVGBFrameSource[i]->OpenReader(&m_pVGBFrameReader[i]);
if (SUCCEEDED(hr))
{
OutputDebugString(L"reader opened\n");
}
else
{
OutputDebugString(L"No reader\n");
}
}
}
//function to find the available gesture count, add the available gestures to source
void CBodyBasics::CheckGestureMultiple()
{
HRESULT hr;
hr = m_pGestureDatabase->get_AvailableGesturesCount(&iGestureCount);
//gesture count
if (SUCCEEDED(hr))
{
OutputDebugString(L"gesture #\n");
}
else
{
OutputDebugString(L"0 gesture\n");
}
StringCchPrintf(smessage, _countof(smessage), L"Gesture Count %d \n", iGestureCount);
OutputDebugString(smessage);
hr = m_pGestureDatabase->get_AvailableGestures(iGestureCount, m_pgesturelist);
//gesture list
if (SUCCEEDED(hr) && m_pgesturelist != nullptr)
{
OutputDebugString(L"received available gestures\n");
GestureType mtype;
WCHAR sName[260];
for (int i = 0; i < iGestureCount; ++i)
{
hr = m_pgesturelist[i]->get_GestureType(&mtype);
//gesture type
if (SUCCEEDED(hr))
{
OutputDebugString(L"gesture type\n");
if (mtype == GestureType_Discrete)
{
OutputDebugString(L"discrete\n");
}
else if (mtype == GestureType_Continuous)
{
OutputDebugString(L"continuous\n");
}
}
else
{
OutputDebugString(L"no gesture type\n");
}
hr = m_pgesturelist[i]->get_Name(260, sName);
//gesture name
if (SUCCEEDED(hr))
{
OutputDebugString(L"gesture name\n");
StringCchPrintf(smessage, _countof(smessage), L"%s \t %d \n", sName, i);
OutputDebugString(smessage);
}
else
{
OutputDebugString(L"no name\n");
}
}
}
else
{
OutputDebugString(L"not received gestures\n");
}
for (int i = 0; i < BODY_COUNT; ++i)
{
//add gestures to the source
hr = m_pVGBFrameSource[i]->AddGestures(iGestureCount, m_pgesturelist);
if (SUCCEEDED(hr))
{
OutputDebugString(L"Gesture added\n");
}
else
{
OutputDebugString(L"not added\n");
}
}
}
//function to read the vgb frame and process if the gesture is detected
void CBodyBasics::ReadVGBFrameMultiple(int t_bodyCount)
{
HRESULT hr;
for (int i = 0; i < t_bodyCount; i++)
{
hr = m_pVGBFrameReader[i]->CalculateAndAcquireLatestFrame(&m_pVGBFrame);
if (SUCCEEDED(hr) && m_pVGBFrame != nullptr)
{
OutputDebugString(L"vgb frame\n");
BOOLEAN bGestureTracked = false;
hr = m_pVGBFrame->get_IsTrackingIdValid(&bGestureTracked);
if (SUCCEEDED(hr) && bGestureTracked)
{
OutputDebugString(L"valid gesture id and gesture tracked\n");
GestureType gesturetype;
m_pgesturelist[i]->get_GestureType(&gesturetype);
switch (gesturetype)
{
case GestureType::GestureType_Discrete:
hr = m_pVGBFrame->get_DiscreteGestureResult(m_pgesturelist[i], &m_pDiscreteResult);
if (SUCCEEDED(hr) && m_pDiscreteResult != nullptr)
{
OutputDebugString(L"discrete result\n");
BOOLEAN bDetected = false;
hr = m_pDiscreteResult->get_Detected(&bDetected);
if (SUCCEEDED(hr) && bDetected)
{
OutputDebugString(L"gesture detected\n");
StringCchPrintf(smessage, _countof(smessage), L"gesture detected %d\n", bDetected);
OutputDebugString(smessage);
float confidence = 0.0f;
hr = m_pDiscreteResult->get_Confidence(&confidence);
if (SUCCEEDED(hr) && confidence != 0.0f)
{
OutputDebugString(L"confidence is detected \t");
//if (confidence > 0.3f)
//{
StringCchPrintf(smessage, _countof(smessage), L"confidence value is %f\n", confidence);
OutputDebugString(smessage);
//}
}
else
{
OutputDebugString(L"no confidence\n");
}
}
else
{
OutputDebugString(L"gesture not detected\n");
}
}
else
{
OutputDebugString(L"no discrete result\n");
}
case GestureType::GestureType_Continuous:
hr = m_pVGBFrame->get_ContinuousGestureResult(m_pgesturelist[i], &m_pContinousResult);
if (SUCCEEDED(hr) && m_pContinousResult != nullptr)
{
OutputDebugString(L"continuous gesture\n");
WCHAR gesturename[100];
hr = m_pgesturelist[i]->get_Name(100, gesturename);
//if (gesturename == L"SwipeLeft")
//{
float progress = 0.0f;
OutputDebugString(L"get progress of continuous gesture \t");
hr = m_pContinousResult->get_Progress(&progress);
if (SUCCEEDED(hr) && progress != 0.0f)
{
StringCchPrintf(smessage, _countof(smessage), L"progress value is %f\n", progress);
OutputDebugString(smessage);
}
//}
}
else
{
OutputDebugString(L"no continuous gesture\n");
}
default:
break;
}
}
else
{
OutputDebugString(L"invalid gesture id\n");
}
}
else
{
OutputDebugString(L"no frame\n");
}
}
}
Is there wrong in my code? Do I need to modify my code? Can anyone help me?

Drag and drop in unity 2d

I am trying to implement drag and drop functionality for my game in unity 2d. I have multiple copies of same object in my screen and they differ only by collider name. I attached the same script to them. Here is a piece of my code
function Start () {
playerTouches = [-1, -1];
}
function resetPlayer(touchNumber: int) {
for(var i = 0; i < playerTouches.length; ++i) {
if(touchNumber == playerTouches[i]) {
playerTouches[i] = -1;
}
}
}
function getCollider(vec: Vector2) {
var ray : Ray = Camera.main.ScreenPointToRay(vec);
var hit : RaycastHit2D = Physics2D.Raycast(ray.origin, ray.direction);
if (hit) {
if (hit.collider != null) {
Debug.Log(hit.collider.name);
return hit.collider.name;
} else {
Debug.Log("is null");
return "null";
}
} else {
Debug.Log("empty");
return "";
}
return "";
}
function processTouch(touch: Touch, touchNumber: int) {
if(touch.phase == TouchPhase.Began) {
var colliderName: String = getCollider(touch.position);
if(colliderName == "Object01" && playerTouches[0] == -1) {
playerTouches[0] = touchNumber;
} else if(colliderName == "Object02" && playerTouches[1] == -1) {
playerTouches[1] = touchNumber;
}
} else if(touch.phase == TouchPhase.Moved) {
// get object and change coords
} else if(touch.phase == TouchPhase.Ended || touch.phase == TouchPhase.Canceled) {
resetPlayer(touchNumber);
}
}
function Update() {
if(Input.touchCount > 0) {
//Debug.Log("count = " + Input.touchCount);
for(var i = 0; i < Input.touchCount; i++)
{
processTouch(Input.GetTouch(i), i);
//Debug.Log("touch : " + i + " " + Input.GetTouch(i).position);
}
}
}
For now I'm detecting on which object user touch. I need to be able to get that object and change it's position.
I also found this code snippet which allows to move rigidbody
var touchDeltaPosition: Vector2 = touch.deltaPosition;
var touchPosition: Vector2;
touchPosition.Set(touchDeltaPosition.x, touchDeltaPosition.y);
rigidbody2D.transform.position = Vector2.Lerp(transform.position, touchPosition, Time.deltaTime * spd);
but it moves all objects regardless of what object I select.
Well, you can do like this. If you have 12 copies of same object and want to move the object which selected by user. So when user Touches the object Change that GameObject tag or Name to another tag. Afterward you can use the some Conditional Statement to work with your code.
Example :
if(Input.GetMouseButtonDown(0)) {
Debug.Log("Mouse is down");
Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);
RaycastHit hitInfo = new RaycastHit();
//bool hit = Physics.Raycast (Camera.main.ScreenPointToRay (Input.mousePosition), out hitInfo);
if(Physics.Raycast(ray, out hitInfo, 30)) {
Debug.Log("Hit " + hitInfo.transform.gameObject.name);
if(hitInfo.transform.gameObject.tag == "Deselected") {
//Debug.Log ("It's working! Attaching MoveCube Script to game :" + hitInfo.transform.gameObject.tag);
findObject = GameObject.FindGameObjectsWithTag("Deselected");
foreach(GameObject go in findObject) {
//go.gameObject.renderer.material.color = Color.white;
go.GetComponent<MoveCube>().enabled = false;
if(hitInfo.transform.gameObject.name.Equals(go.gameObject.name)) {
//hitInfo.transform.renderer.material.color = Color.white;
hitInfo.transform.gameObject.GetComponent<MoveCube>().enabled = true;
changeTAG = true;
} else {
hitInfo.transform.gameObject.tag = "Deselected"
}
}
playerObject = GameObject.FindGameObjectsWithTag("Player");
foreach(GameObject game in playerObject) {
count++;
if(count == 1) {
hitInfo.transform.gameObject.tag = "Player";
}
if(count >= 1) {
game.gameObject.tag = "Deselected";
game.gameObject.GetComponent<MoveCube>().enabled = false;
//game.gameObject.renderer.material.color = Color.white;
}
}
if(changeTAG) {
hitInfo.transform.gameObject.tag = "Player";
/*if (hitInfo.transform.gameObject.GetComponent<Rigidbody> ()) {
Debug.Log ("RigidBody is already added Can't add another RigidBody");
hitInfo.transform.rigidbody.WakeUp ();
} else {
hitInfo.transform.gameObject.AddComponent<Rigidbody> ().useGravity = false;
// hitInfo.transform.gameObject.GetComponent<Rigidbody> ().WakeUp ();
}*/
changeTAG = false;
} else if(!changeTAG) {
hitInfo.transform.gameObject.tag = "Deselected";
}
} else {
Debug.Log("Not Working");
}
} else {
Debug.Log("No hit");
}
Debug.Log("Mouse is down");
}
The above code is for Change the tag for selected and deselected cube. After that you can easily identify the Selected gameObject and can move it where ever you want.
You can use this code in the Update function.

Limiting JASidePanel Center panel slide

Hi I am using JASidePanels to create the left menu style but I want to do some particular changes.
The first is that I want the menu to have only 115 px wide. And for that I have found:
self.leftFixedWidth = 115;
Ok but, I can still slide the center panel to the right and see whats "hidden". Is there a way to fix its maximum position at 115 px? Just like the facebook menu?
go to JASidePanelController.m
find - (CGFloat)_correctMovement:(CGFloat)movement; method, replace it with this:
- (CGFloat)_correctMovement:(CGFloat)movement {
CGFloat position = _centerPanelRestingFrame.origin.x + movement;
if (self.state == JASidePanelCenterVisible) {
if (self.state != JASidePanelLeftVisible) {
if (position >= self.leftVisibleWidth) {
return self.leftVisibleWidth;
}
}
else if ((position > 0.0f && !self.leftPanel) || (position < 0.0f && !self.rightPanel)) {
return 0.0f;
}
} else if (self.state == JASidePanelRightVisible && !self.allowRightOverpan) {
if ((position + _centerPanelRestingFrame.size.width) < (self.rightPanelContainer.frame.size.width - self.rightVisibleWidth)) {
return 0.0f;
} else if (position > self.rightPanelContainer.frame.origin.x) {
return self.rightPanelContainer.frame.origin.x - _centerPanelRestingFrame.origin.x;
}
} else if (self.state == JASidePanelLeftVisible && !self.allowLeftOverpan) {
if (position > self.leftVisibleWidth) {
return 0.0f;
} else if (position < self.leftPanelContainer.frame.origin.x) {
return self.leftPanelContainer.frame.origin.x - _centerPanelRestingFrame.origin.x;
}
}
return movement;
}

cocos2d content.size, boundingBox and size

im writing a game to find the differences between 2 images. i created a subclass of CCSprite, Spot. firstly i tried to create small images and add itself according to it's position, but later i found the position is hard to determine, since it's hard to avoid offset of 1 or 2 pixels.
then i tried to make the Spot the same size as the image, with the other part transparent. but I still need to find out the 'hotspot' of finger tap. but when i use CGRectContainsPoint([self boundingBox], touchLocation), it's actually the whole image.
so is there any other way to do this? like content.size or self.size, and make a CGRect out of it's non-transparent part?
Thank you.
I figured it out now. here is my code: (it's actually quite simple
-(void) findRect:(NSString*) fn {
//the origin of mTex is top left
//the origin of CGRect is top left, in the coordinate system inside the image
int topLeftX = 0;
int topLeftY = 0;
for (int i = 0; i < image_width; i += 10) {
for (int j = 0; j < image_height; j += 10) {
if (([mTex pixelAt:ccp(i, j)].a & 0xFF) != 0) {
topLeftX = i;
topLeftY = j;
goto outer;
}
}
}
outer:;
int topRightX = 0;
for (int i = topLeftX; i < image_width; i += 10) {
if (([mTex pixelAt:ccp(i, topLeftY)].a & 0xFF) == 0) {
topRightX = i;
break;
}
}
if (topRightX == 0) {
topRightX = image_width - 1;
}
int bottomLeftY = 0;
for (int i = topLeftY; i < image_height; i += 10) {
if (([mTex pixelAt:ccp(topLeftX, i)].a & 0xFF) == 0) {
bottomLeftY = i;
break;
}
}
if (bottomLeftY == 0) {
bottomLeftY = image_height - 1;
}
areaRect = CGRectMake(topLeftX, topLeftY, topRightX - topLeftX, bottomLeftY - topLeftY);
}