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

2014/10/26

A complicated problem with scores

GreenCompiler GreenCompiler

2014/10/26

#
I'm currently making a game for a school project and I've run into a problem that I just can't seem to get rid of. It takes some time and a few code fragments to explain so please bare with me. The problem I have is that I need to get a number of points from an enemy that I shoot down and send that to the scoreboard which will display it either when the game is over or when the level is complete. I have the method that gets the points from each enemy when it is shot down, but I have no idea how to send it to the Scoreboard object in my game. I had the idea to send the points into an array list as well as the variable that would determine from which type of enemy it came from. I know this sounds rather overcomplicated but it needs to be done because the scoreboard I have will display the scores per type of enemy I shoot down in the game and then combine everything to get the total score. Below here are the code fragments that apply to this problem. Please note that a part of my code is incomplete as I had absolutely no idea what to do. This first piece of code if from a specific type of enemy.
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
public class Enemy1 extends Enemy
{
    private GreenfootImage image1;
    private GreenfootImage image2;
    private GreenfootImage image3;
    private GreenfootImage image4;
    private GreenfootImage image5;
     
    public Enemy1()
    {
        points = 100;
    }
     
    /**
     * Act - do whatever the Enemy1 wants to do. This method is called whenever
     * the 'Act' or 'Run' button gets pressed in the environment.
     */
    public void act()
    {
        enemyFireBlue();
        moveEnemy();
        enemyAnimate();
        getPoints();
    }
    private int firecounter = 50;
    private void enemyFireBlue()
    {
        firecounter++;
        if(firecounter == 100)
        {
            World world;
            BlueBullet bullet1 = new BlueBullet();
            getWorld().addObject(bullet1, getX()-50, getY());
            firecounter = 0;
        }
    }
    private int timer = 0;
    private void enemyAnimate()
    {
        image1 = new GreenfootImage("Ship1-1.png");
        image2 = new GreenfootImage("Ship1-2.png");
        image3 = new GreenfootImage("Ship1-3.png");
        image4 = new GreenfootImage("Ship1-4.png");
        image5 = new GreenfootImage("Ship1-5.png");
        timer++;
         
        if(timer == 1)
        {
            setImage(image1);
        }
        if(timer == 10)
        {
            setImage(image2);
        }
        if(timer == 20)
        {
            setImage(image3);
        }
        if(timer == 30)
        {
            setImage(image4);
        }
        if(timer == 40)
        {
            setImage(image5);
        }
        if(timer == 50)
        {
            timer = 0;
        }
    }
}
The second piece of code is from the class Enemy that holds all subclasses of the enemy types.
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
public class Enemy extends Actor
{
    public int points;
    /**
     * This class is set here to move the enemies around.
     */
    public void moveEnemy()
    {
        move(-2);
        int left = 25 - Greenfoot.getRandomNumber(50);
    }
    public void moveEnemyDown()
    {
        setLocation(getX(), getY()+1);
    }
    public void moveEnemyUp()
    {
        setLocation(getX(), getY()-1);
    }
    public void moveEnemySurprise()
    {
        move(2);
        int left = 25 - Greenfoot.getRandomNumber(50);
    }
     
    public void moveEnemyFast()
    {
        move(-4);
        int left = 25 - Greenfoot.getRandomNumber(50);
    }
    public void moveEnemyDownFast()
    {
        setLocation(getX(), getY()+2);
    }
    public void moveEnemyUpFast()
    {
        setLocation(getX(), getY()-2);
    }
    public void moveEnemySurpriseFast()
    {
        move(4);
        int left = 25 - Greenfoot.getRandomNumber(50);
    }
     
    public void moveEnemyRapid()
    {
        move(-6);
        int left = 25 - Greenfoot.getRandomNumber(50);
    }
    public void moveEnemyDownRapid()
    {
        setLocation(getX(), getY()+3);
    }
    public void moveEnemyUpRapid()
    {
        setLocation(getX(), getY()-3);
    }
    public void moveEnemySurpriseRapid()
    {
        move(6);
        int left = 25 - Greenfoot.getRandomNumber(50);
    }
     
    public int getPoints()
    {
        return points;
    }
}
The third piece of code is of the class Bullet. This is where my problem starts. In the eatEnemy method I have the code needed to request the points from the enemy when the PlayerBullet (my bullets) shoot the enemy down. I however have no idea how to send it to the Scoreboard object and put it into an array list. NOTE: since the bullet class is very long, I only added the eatEnemy method into this code piece.
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
public class Bullet extends Actor
{
(other code. Not important for this problem.)
    public void eatEnemy()
    {
        Actor enemy = getOneIntersectingObject(Enemy.class);
        if(enemy != null)
        {
            World world;
            String whatEnemy = enemy.getClass().getName();
            if (whatEnemy.equals("Enemy1"))
            {
                Enemy1 e = new Enemy1();
                LevelCompletedScoreboard s = new LevelCompletedScoreboard();
                //somehow, send scores to LevelCompleteScoreboard.
                System.out.println(e.getPoints());
            }
            if (whatEnemy.equals("Enemy2"))
            {
                Enemy2 e = new Enemy2();
                System.out.println(e.getPoints());
            }
            if (whatEnemy.equals("Enemy3"))
            {
                Enemy3 e = new Enemy3();
                System.out.println(e.getPoints());
            }
            if (whatEnemy.equals("Enemy4"))
            {
                Enemy4 e = new Enemy4();
                System.out.println(e.getPoints());
            }
            if (whatEnemy.equals("Enemy5"))
            {
                Enemy5 e = new Enemy5();
                System.out.println(e.getPoints());
            }
            if (whatEnemy.equals("Enemy6"))
            {
                Enemy6 e = new Enemy6();
                System.out.println(e.getPoints());
            }
            if (whatEnemy.equals("Enemy7"))
            {
                Enemy7 e = new Enemy7();
                System.out.println(e.getPoints());
            }
            if (whatEnemy.equals("Enemy8"))
            {
                Enemy8 e = new Enemy8();
                System.out.println(e.getPoints());
            }
            if (whatEnemy.equals("Enemy9"))
            {
                Enemy9 e = new Enemy9();
                System.out.println(e.getPoints());
            }
            if (whatEnemy.equals("Enemy10"))
            {
                Enemy10 e = new Enemy10();
                System.out.println(e.getPoints());
            }
            if (whatEnemy.equals("Enemy11"))
            {
                Enemy11 e = new Enemy11();
                System.out.println(e.getPoints());
            }
            if (whatEnemy.equals("Enemy12"))
            {
                Enemy12 e = new Enemy12();
                System.out.println(e.getPoints());
            }
            if (whatEnemy.equals("Enemy13"))
            {
                Enemy13 e = new Enemy13();
                System.out.println(e.getPoints());
            }
            if (whatEnemy.equals("Enemy14"))
            {
                Enemy14 e = new Enemy14();
                System.out.println(e.getPoints());
            }
            if (whatEnemy.equals("Enemy15"))
            {
                Enemy15 e = new Enemy15();
                System.out.println(e.getPoints());
            }
            if (whatEnemy.equals("Enemy16"))
            {
                Enemy16 e = new Enemy16();
                System.out.println(e.getPoints());
            }
            if (whatEnemy.equals("Enemy17"))
            {
                Enemy17 e = new Enemy17();
                System.out.println(e.getPoints());
            }
            if (whatEnemy.equals("Enemy18"))
            {
                Enemy18 e = new Enemy18();
                System.out.println(e.getPoints());
            }
            if (whatEnemy.equals("Enemy19"))
            {
                Enemy19 e = new Enemy19();
                System.out.println(e.getPoints());
            }
            if (whatEnemy.equals("Enemy20"))
            {
                Enemy20 e = new Enemy20();
                System.out.println(e.getPoints());
            }
            Explosion Explosion = new Explosion();
            Greenfoot.playSound("blast.mp3");
            getWorld().removeObject(enemy);
            getWorld().removeObject(this);
        }
    }
}
The code from the Scoreboard object that I have is pretty much nothing yet since I haven't been able to continue with that because of the problem that I have. What I have are two array lists, but no idea how to add things into them or pick matches from the lists for each enemy type and display them at the proper location on the Scoreboard object.
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
public class LevelCompletedScoreboard extends Actor
{
    public ArrayList<String> kills = new ArrayList<String>();
    public ArrayList<String> scores = new ArrayList<String>();
     
    /**
     * Act - do whatever the LevelCompletedScoreboard wants to do. This method is called whenever
     * the 'Act' or 'Run' button gets pressed in the environment.
     */
    public void act()
    {
        AddScore();
        AddKills();
    }   
     
    public void AddScore()
    {
        //Add code here.
    }
     
    public void AddKills()
    {
        //Add code here.
    }
}
Any advice is more than welcome as I have been breaking my head over this problem for nearly two weeks already. If there is any code that I have missed for you to get a more clear view of the problem then please say so and I'll post it in a reply.
Super_Hippo Super_Hippo

2014/10/26

#
First, all lines like this 'int left = 25 - Greenfoot.getRandomNumber(50);' in your Enemy class are useless as they are right now. You create an int that is lost after the method which ends after this line. Now to the Bullet class. It is not very efficient to get the points for an enemy with creating a new one.
1
2
3
4
5
6
Enemy enemy = (Enemy) getOneIntersectingObject(Enemy.class);
if (enemy != null)
{
    (Worldname) w = (Worldname) getWorld();
    w.getScoreboard().addScore(enemy.getType(), enemy.getPoints()); //since your point field in Enemy is public, you can also just write enemy.points
}
For the Scoreboard. You need to create a scoreboard at the beginning. Then you can add everything to it when killing an enemy. And in the end, you add this scoreboard to the world. Also you don't need two different scoreboards (as you probably have because it is named LevelCompletedScoreboard). Just name it Scoreboard.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
//in world
private Scoreboard scoreboard;
 
//in constructor
scoreboard = new Scoreboard();
//don't add it to the world yet
 
 
//when the level is over
addObject(scoreboard, /* x,y */);
 
public Scoreboard getScoreboard()
{
    return scoreboard;
}
Are the enemies very different to each other? In most cases, you don't need to create a subclass for each enemy. Instead, you can just pass an int when creating it and save this int. Then you can use it if there are differences in the moving and so on. Then you can also access the type of enemy easier than you did above. If you want to keep all the subclasses, you can still have a 'type' int in your Enemy class where you hold the type. What exactly do you want to do with the array lists? The Scoreboard won't add anything to it itself, so you can remove the calls to the methods from the act method. First, you probably don't want to store Strings in the Lists. What exactly do you want to have displayed? How often every type of enemy was killed and the total score? If yes, you can use a fixed array for that with 20 cells. You can either have one array and calculate the points later, or you have two arrays like you wanted to do it.
1
2
3
4
5
6
7
8
private int kills[] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}; //well, there is probably an easier way to achieve this, but it should also work like this :)
private int score[] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0};
 
public void addScore(int type, int points)
{
    kills[type] = kills[type]+1; //I am not sure if 'kills[type]++' would work here, you can try it out
    score[type] = score[type]+points;
}
GreenCompiler GreenCompiler

2014/10/26

#
Thank you for your reply. It has helped me quite a bit already. There are however still a few things that I'm running into when using the method that you suggested.
For the Scoreboard. You need to create a scoreboard at the beginning. Then you can add everything to it when killing an enemy. And in the end, you add this scoreboard to the world. Also you don't need two different scoreboards (as you probably have because it is named LevelCompletedScoreboard). Just name it Scoreboard.
The game I'm working on has multiple worlds. When using your method as you described in the code fragment then I would create a Scoreboard object at the start of the world named "Level1" in that level I would be gunning down the different types of enemies. That would indeed work, but at the end of each level there is a portal object that leads to a new world, the boss area. I'm not sure if the Scoreboard object made in the beginning of world Level1 would be able to be transported with all of its data to the world BossArea1. It doesn't just end there though. After the boss is defeated, another portal object will appear that leads to the world named "LevelCompletedScreen" and that is the world where I actually want the scoreboard to be displayed. This also brings me to another part of your post.
Are the enemies very different to each other?
Yes, they are quite different to each other. they all have a different set of images for animation, fire different bullets and move at different speeds.
What exactly do you want to have displayed? How often every type of enemy was killed and the total score?
I think it's best if I show you with a picture of the scoreboard I want to use. If the link I posted worked, you'll see how the scoreboard I'd like to use looks like. In it, you see the different types of enemies that I want to use and there is also a field for the boss as well as the total score. I want to have the number of kills per type of enemy displayed just above the orange field for the score per enemy. (The boss won't have a kill count) I want to have the total score her type of enemy displayed inside of the orange field. In the field of the total score, I want to have all of the scores per enemy type added up to each other.
Also you don't need two different scoreboards (as you probably have because it is named LevelCompletedScoreboard). Just name it Scoreboard.
The thing about that is that I want to use two scoreboards for this game. The first scoreboard will be used as described above and it will be displayed after each level and the connecting boss area has been completed. The second scoreboard will be used to only display the total score and it will be one of those boards where you can enter a name and get into a ranking list of 1 to 10 if your score is high enough.
Super_Hippo Super_Hippo

2014/10/26

#
You have to put the link of the image inside the 'img' tags, not the website on which the image is displayed beside other things. If you look at this game, you will see quite many different types of enemies which also act differently from each other. They are all one class though. You can pass the scoreboard to every new level. As an alternative, you can make everything in the scoreboard 'static'. To call the method later then, just use 'Scoreboard.addScore(enemy.getType(), enemy.getPoints());'. Static values are bounded to the class, not to an object, so you can create the scoreboard when it has to be displayed. Changing worlds doesn't change anything then. (Make sure you reset all values when the game is reset.)
GreenCompiler GreenCompiler

2014/10/26

#
thanks for showing the image. I made a small mistake with the link there.
GreenCompiler GreenCompiler

2014/10/26

#
I have a few small problems with the code fragments that you posted in your initial reply. Greenfoot is giving me error after error and I'm just about to reach the end of my patience with this. I'm sorry to ask, but could you perhaps provide me with some more advice on how to properly solve this problem? I have only started using Greenfoot a very short while ago and the Java language is also very new to me.
Super_Hippo Super_Hippo

2014/10/26

#
The best thing would be that you show what you tried and I/we will try to solve the errors one after another.
GreenCompiler GreenCompiler

2014/10/27

#
I found the problem that was bugging me so much after checking every last line of my code yesterday and I've been able to fix it with the code that you provided. I am still wondering though, how do I pass the scoreboard on to every new level?
Super_Hippo Super_Hippo

2014/10/27

#
As I noted earlier, it would be easier to use static values in the Scoreboard class, so you can access them from everywhere without problems and they are not fixed to an instance of the class. If you want to pass the object, you can save the object in your world and when change the world, you pass the object. To do this, you have to modify the constructor of the world to which the scoreboard has to be passed. As an alternative, you could also pass the world. Only because a world is not active anymore, it is not "lost". If you have a reference to it, you can still access variables and methods. And you can even set the same world active again, not a new instance of it. (Although this has nothing to do with your problem right now.)
GreenCompiler GreenCompiler

2014/10/31

#
Thanks for all the advice you've given me. ^^ I've been able to solve the problems I had now. It's still a bit rough around the edges but my game is doing what its supposed to do now.
You need to login to post a reply.