This site requires JavaScript, please enable it in your browser!
Greenfoot back
kartikitrak
kartikitrak wrote ...

2012/6/5

Linking broken chains

kartikitrak kartikitrak

2012/6/5

#
I have a bunch of objects that are spawned in a certain location and aren't neccesary linked together in a chain. I was wondering how I would link the certain objects in a chain and if I broke the chain, how it could be repaired by relinking it to the closest unlinked part. For example From this: *** ******* To this: ************ This is the code for spawning the enemies.
public void addEnemy()
    {
        for (int i = 0; i<numOfEnemies; i++)
        {
            typesOfEnemies = Greenfoot.getRandomNumber(3)+1;
            if (spawnDelay == 50)
            {
                if (typesOfEnemies == 1)
                {
                    eBlueStar[i] = new EnemyBlueStar(); 
                    addObject(eBlueStar[i], 110,125);  
                }
                if (typesOfEnemies == 2)
                {
                    eYellowTriangle[i] = new EnemyYellowTriangle(); 
                    addObject(eYellowTriangle[i], 110,125);  
                }
                if (typesOfEnemies == 3)
                {
                    eGreenSquare[i] = new EnemyGreenSquare(); 
                    addObject(eGreenSquare[i], 110,125);  
                }
                spawnDelay = 0;
            }
        }
    }
Duta Duta

2012/6/6

#
How do you want them to be linked? With a line between them, or what?
danpost danpost

2012/6/6

#
I will assume you have a super-class called Enemy (which have EnemyGreenSquare, EnemyYellowTriangle and EnemyBlueStar as sub-classes). Add as a Enemy class a variable (as static) called lastSpawned and set it to null. Add, also, two instance object Enemy variables to hold the Enemy objects on either side of any instance Enemy object. The variables should be as follows:
static Enemy lastSpawned = null;
Enemy frontEnemy = null;
Enemy backEnemy = null;
Now, in the constructor of Enemy add the following:
if (lastSpawned != null) lastSpawned.backEnemy = this;
frontEnemy = lastSpawned;
lastSpawned = this;
You can now use 'getObjectsInRange(getImage(),getWidth() / 2 + 1)' and compare the objects in the list returned to 'frontEnemy'. If not in the list, double move else normal move. You will have to check to see if 'frontEnemy' is null first (meaning, it is the lead Enemy, at the moment). You will also have to adjust the values of 'frontEnemy' and 'backEnemy' of Enemy instances when one is removed. If the collision checking is in the Enemy class, then:
getWorld().removeObject(this);
if (frontEnemy != null) frontEnemy.backEnemy = backEnemy;
if (backEnemy != null) backEnemy.frontEnemy = frontEnemy;
That should do it!
kartikitrak kartikitrak

2012/6/7

#
Is this right? This part is the variable and the constructor in the Enemy parent class
 private Enemy frontEnemy = null;
    private Enemy backEnemy = null;

    public Enemy()
    {
        if (lastSpawned != null) lastSpawned.backEnemy = this;
        frontEnemy = lastSpawned;
        lastSpawned = this;
    }
This part is in each enemy shape(subclass to Enemy)
public void checkLink(Enemy frontEnemey, Enemy backEnemy)
    {

        getWorld().removeObject(this);
        if (frontEnemy != null) frontEnemy.backEnemy = backEnemy;
        if (backEnemy != null) backEnemy.frontEnemy = frontEnemy;
    }
danpost danpost

2012/6/7

#
The second part should also go in the Enemy class and called from the sub-classes (it will be an inherited method). I would give it a more appropriate name, though; and the parameters are not needed. Use something like this:
public void removeThisEnemy()
{
    getWorld().removeObject(this);
    if (frontEnemy != null) frontEnemy.backEnemy = backEnemy;
    if (backEnemy != null) backEnemy.frontEnemy = frontEnemy;
}
and you can call it from the sub-classes with
removeThisEnemy();
The call should replace your collision check 'getWorld().removeObject(this);' statement.
kartikitrak kartikitrak

2012/6/7

#
Then in the enemyshapes I would make a getObjectsInRange method that checks if there is a gap between the shapes and if there is, make the speed increase and when it is in range decrease speed and link. Is this correct?
danpost danpost

2012/6/7

#
The movement will be the same for all three shapes; so, its code should also be in the Enemy class. The 'linking' should now be taken care of. It is now just a matter of speeding up those that have a frontEnemy object that is not in range.
kartikitrak kartikitrak

2012/6/7

#
Danpost. You used this code for your line follower however I'm having a very hard time understanding how you controlled the speed here.
int dx = ((3 - direction) % 2) * (1 - direction), dy = ((4 - direction) % 2) * (2 - direction);
        // follow the edge of the color only if already on the color
        if (getWorld().getBackground().getColorAt(getX(), getY()).equals(Color.blue))
        {
            // the following loop checks 'hand' direction first, then forward, the opposite of hand,
            // and finally back from whence we came.
            for (int i = 0; i < 4; i++)
            {
                // the following sets the new direction to look
                int newDir = (direction + hand - i * hand + 4) % 4;
                // the following line breaks the new direction up into its x and y values
                int ndx = ((3 - newDir) % 2) * (1 - newDir), ndy = ((4 - newDir) % 2) * (2 - newDir);
                // if not moving outside world boundaries, check for blue color
                if (getX() + ndx >= 0 && getX() + ndx < getWorld().getWidth() &&
                getY() + ndy >= 0 && getY() + ndy < getWorld().getHeight() &&
                getWorld().getBackground().getColorAt(getX() + ndx, getY() + ndy).equals(Color.blue))
                {
                    setLocation(getX() + ndx, getY() + ndy);
                    direction = newDir;
                    return;
                }
            }
        }
        // the following only gets executed if the color has not been found yet
        // or, if found, it was only one pixel, with no connectors.
        if (getX() + dx >= 0 && getX() + dx < getWorld().getWidth() && getY() + dy >= 0 && getY() + dy < getWorld().getHeight())
        {
            setLocation(getX() + dx, getY() + dy);
            if (getWorld().getBackground().getColorAt(getX(), getY()).equals(Color.blue))
                direction = (direction + hand + 4) % 4;
        }
        else direction = (direction + hand + 4) % 4;
danpost danpost

2012/6/7

#
In essence, the speed is a one pixel move. You are looking at the 4-way move method. The 'direction' field carries a value from 0 to 3; 0 = right, 1 = down, 2 = left and 3 is up. The first line in the code you posted breaks the value of 'direction' into the x and y offsets for a move in that direction; if direction = 0 (right), dx = 1 and dy = 0; if 1 (down) dx = 0 and dy = 1; if 2 (left), dx = -1 and dy = 0; and if 3, dx = 0 and dy = -1. The following 'if' block finds the next location to move to if already on the color of choice. The above was basically to clarify what the code was doing. But, anyway, you want to control the speed of the actors. This is fairly simple. Use this for the code to move (in the act() of Enemy class):
move();
if (frontEnemy != null && !frontEnemyInRange()) move():
Then create the method 'frontEnemyInRange()':
private boolean frontEnemyInRange()
{
    if (frontEnemy == null) return;
    for (Object obj : getObjectsInRange(getImage().getWidth() + 1, Enemy Class))
    {
        Enemy enemy = (Enemy) obj;
        if (frontEnemy.equals(enemy)) return true;
    }
    return false;
}
I think I coded that right (I did it on the fly -- unchecked).
You need to login to post a reply.