Pages: [1]
|
|
|
Author
|
Topic: My first AI (Read 1898 times)
|
jaychant
*Smell* controller
Offline
Gender:
Posts: 432
Please visit my homepage
|
It's been a few days now since I finished my first (non-random) AI. It's very simple; it tells Mario how to kill several goombas coming out of four doors indefinitely.
The unfortunate thing is, I haven't seen a whole lot of constructive criticism on my AI.
Here is a download (includes the source code, the MarioAI.gmk) Here is the game on YoYo Games (no download necessary)
What I want you to do is observe the AI (and perhaps peek at the code for the AI below) and give me constructive criticism.
On a side note, the game is actually kind of fun and somehow addicting. I don't know why. (Perhaps because it's an arcade game?)
Here is the code for the AI (Note: GML is very similar to C++.): (Note: This happens every frame, or in GM terms, at every Begin Step)
/* This code is the AI handler. It simulates button presses in order to create simplicity. The AI presses a key depending on the situation. This prevents pointless checks already checked for in the Step event. The AI is turned off when the player chooses to do so (allowing him to assume control), which is controlled by the variable ai_active */
if (ai_active = false) { exit; }
number_below = 0;
//Declaring variable nearest_goomba. Gets deleted after the code execution. var nearest_goomba;
//Sets variable nearest_goomba to the ID of the nearest goomba object if (instance_exists(obj_goomba)) {nearest_goomba = instance_nearest(x,y,obj_goomba);}
//Set how many goombas are below Mario; Used later with (obj_goomba) { if (below = true) { other.number_below += 1; } }
//Danger zone; If goombas are too close, but just on one side, Mario will flee. //If Mario is surrounded, he will jump instead. if (collision_rectangle(x,y+8,x+20,y+20,obj_goomba,false,true)) //Collision_rectangle checks for collisions within a rectangle. { if not (collision_rectangle(x,y+8,x-20,y+20,obj_goomba,false,true)) and not (collision_point(x-20,y,obj_block,false,true)) { //keyboard_key simulates the pressing or releasing of a key on the keyboard keyboard_key_press(vk_left); keyboard_key_release(vk_right); } else { keyboard_key_press(vk_up); keyboard_key_release(vk_up); } } else if (collision_rectangle(x,y+8,x-20,y+20,obj_goomba,false,true)) { if not (collision_point(x+20,y,obj_block,false,true)) { keyboard_key_press(vk_right); keyboard_key_release(vk_left); } else { keyboard_key_press(vk_up); keyboard_key_release(vk_up); } } //If not in danger zone, move towards nearest goomba (if a goomba //exists) else if (instance_exists(obj_goomba)) { if (nearest_goomba.x > x) { keyboard_key_press(vk_right); keyboard_key_release(vk_left); } else { keyboard_key_press(vk_left); keyboard_key_release(vk_right); } //Check to see if Mario is in range to jump on the goomba. If he is, jump. if (collision_line(x+20,y+8,x+72,y+8,obj_goomba,false,true)) or (collision_line(x-20,y+8,x-72,y+8,obj_goomba,false,true)) { keyboard_key_press(vk_up); keyboard_key_release(vk_up); } } //if the previous statement returns false, there are no goombas. Return to the //center of the screen. (If within a range, stop) else { if (x >= (room_width/2) - 10) and (x <= (room_width/2) + 10) { keyboard_key_release(vk_right); keyboard_key_release(vk_left); } else { if (x > room_width / 2) { keyboard_key_press(vk_left); keyboard_key_release(vk_right); } else { keyboard_key_press(vk_right); keyboard_key_release(vk_left); } } } //If too close to the door, move away if safe. if (x > instance_nearest(x,y,obj_door).x-12) and (x < instance_nearest(x,y,obj_door).x+30) { if (x > instance_nearest(x,y,obj_door).x) { //check that there isn't a goomba close by to the right. //If there is, jump and continue left. Otherwise, change movement //to right. if (collision_rectangle(x,y+8,x+20,y+20,obj_goomba,false,true)) or (x > 515) { keyboard_key_press(vk_up); keyboard_key_release(vk_up); } else { keyboard_key_press(vk_right); keyboard_key_release(vk_left); } } else { //check that there isn't a goomba close by to the left. //If there is, jump and continue right. Otherwise, change movement //to left. if (collision_rectangle(x,y+8,x-20,y+20,obj_goomba,false,true)) or (x < 140) { keyboard_key_press(vk_up); keyboard_key_release(vk_up); } else { keyboard_key_press(vk_left); keyboard_key_release(vk_right); } } }
|
|
|
Logged
|
|
|
|
RTyp06
*Smell* controller
Offline
Posts: 491
|
Let's talk about your code...
if (ai_active = false) { exit; }
I personally would not do it this way. Instead I would write it like this..
if(ai_active == true) { // All your AI code here. } // No need for an exit statement
This will keep all your code aligned so that it is easier to see when a nested if statement starts and stops. It is also easier to understand. Also, a single "=" (equal sign) is usually used as an assignment operator. Whenever you want to compare two or more values, use the "==" double equal sign.
Always keep your local variable, default value assignments together and (usually) at the top of your code. In GML, use the var keyword in this case because you are using a script specific variable that is only used inside this code structure. This way no outside script or object can access this local variable.
So I'd write it like this:
var number_below = 0; And put it as the first line of code.
//Sets variable nearest_goomba to the ID of the nearest goomba object if (instance_exists(obj_goomba)) {nearest_goomba = instance_nearest(x,y,obj_goomba);}
Never put brackets on the same line as the code. This makes it look cluttered and difficult to read. You didn't do it with the rest of your code, I don't know why you did it here.
//Set how many goombas are below Mario; Used later with (obj_goomba) { if (below = true) { other.number_below += 1; } } This does not seem efficient to me. You are checking all goomba object's below variable and generating a count upon this variable. And I assume that each goomba object then must decide if it is above or below at each step. Instead I would perform that check right here in this code structure. Somthing like this:
with(object_goomba) { if y > object_mario.y { number_under +=1; } }
Oh and once again use the == when comparing values not =.
//Danger zone; If goombas are too close, but just on one side, Mario will flee. //If Mario is surrounded, he will jump instead. if (collision_rectangle(x,y+8,x+20,y+20,obj_goomba,false,true)) //Collision_rectangle checks for collisions within a rectangle. { if not (collision_rectangle(x,y+8,x-20,y+20,obj_goomba,false,true)) and not (collision_point(x-20,y,obj_block,false,true)) { //keyboard_key simulates the pressing or releasing of a key on the keyboard keyboard_key_press(vk_left); keyboard_key_release(vk_right); } else { keyboard_key_press(vk_up); keyboard_key_release(vk_up); } } else if (collision_rectangle(x,y+8,x-20,y+20,obj_goomba,false,true)) { if not (collision_point(x+20,y,obj_block,false,true)) { keyboard_key_press(vk_right); keyboard_key_release(vk_left); } else { keyboard_key_press(vk_up); keyboard_key_release(vk_up); } }
Wow.. Ok, let's see...From the Game Maker Help/Manual:
collision_rectangle(x1,y1,x2,y2,obj,prec,notme) This function tests whether there is a collision between the (filled) rectangle with the indicated opposite corners and entities of object obj. For example, you can use this to test whether an area is free of obstacles.
I may have to get back to you on this one as far as using this function to check for nearby goombas. As for the keyboard functions, I personally would not simulate key strokes. I'd directly make mario jump or run as required. it may be more confusing to the outside eye but, if well commented, this should not be a problem.
//If not in danger zone, move towards nearest goomba (if a goomba //exists) else if (instance_exists(obj_goomba)) { if (nearest_goomba.x > x) { keyboard_key_press(vk_right); keyboard_key_release(vk_left); } else { keyboard_key_press(vk_left); keyboard_key_release(vk_right); }
Ok now you have already checked to see if a goomba existed earlier in the code so there should not be a need to check if the instance exists again. In fact you could incorporate that check into the very first if statement...Somthing like this:
if(ai_active == true )&& (instance_exists(obj_goomba) == true) { // All your AI code here. }
//Check to see if Mario is in range to jump on the goomba. If he is, jump. if (collision_line(x+20,y+8,x+72,y+8,obj_goomba,false,true)) or (collision_line(x-20,y+8,x-72,y+8,obj_goomba,false,true)) { keyboard_key_press(vk_up); keyboard_key_release(vk_up); } }
Once again, negative on the simulated keystrokes. This is bad design imo. //if the previous statement returns false, there are no goombas. Return to the //center of the screen. (If within a range, stop) else { if (x >= (room_width/2) - 10) and (x <= (room_width/2) + 10) { keyboard_key_release(vk_right); keyboard_key_release(vk_left); } else { if (x > room_width / 2) { keyboard_key_press(vk_left); keyboard_key_release(vk_right); } else { keyboard_key_press(vk_right); keyboard_key_release(vk_left); } } }
Ok I see why you did the second instance check now. Instead, I'd Simply nest the first instance check and then perform all goomba free actions in the else event to make your code more concise and easy to follow. I'm certain you do not have to check twice to see if any goomba obects exist.
//If too close to the door, move away if safe. if (x > instance_nearest(x,y,obj_door).x-12) and (x < instance_nearest(x,y,obj_door).x+30) { if (x > instance_nearest(x,y,obj_door).x) { //check that there isn't a goomba close by to the right. //If there is, jump and continue left. Otherwise, change movement //to right. if (collision_rectangle(x,y+8,x+20,y+20,obj_goomba,false,true)) or (x > 515) { keyboard_key_press(vk_up); keyboard_key_release(vk_up); } else { keyboard_key_press(vk_right); keyboard_key_release(vk_left); } } else { //check that there isn't a goomba close by to the left. //If there is, jump and continue right. Otherwise, change movement //to left. if (collision_rectangle(x,y+8,x-20,y+20,obj_goomba,false,true)) or (x < 140) { keyboard_key_press(vk_up); keyboard_key_release(vk_up); } else { keyboard_key_press(vk_left); keyboard_key_release(vk_right); } } }
I'll get back to you on this one... I may try and come up with a more efficient version.. sometime...later. btw I've been a hobbyist programmer for years but am in no way an expert and welcome critisisim from anyone more knowledgable than I.
|
|
« Last Edit: January 23, 2009, 01:03:39 am by RTyp06 »
|
Logged
|
|
|
|
jaychant
*Smell* controller
Offline
Gender:
Posts: 432
Please visit my homepage
|
While I appreciate your criticisms on the bad organization I had, I was looking for criticisms on the behavior of the AI and the methods I used. I made a lot of simplications because this wasn't a serious project, just to practice.
|
|
« Last Edit: January 23, 2009, 01:21:29 am by jaychant »
|
Logged
|
|
|
|
Arne
Enlightened
Offline
Gender:
Posts: 520
Yak!
|
I couldn't figure out how to play it in the browser, but I'm blocking quite a bit of stuff.
I prefer to not make the jump to the AI method at all if the AI is off. I.e. I do the check before I jump. If AI is off I have no business there in the first place.
I'm not familiar with GM code, but I like the idea of a virtual joypad (or keyboard). In my current project, I use OOP, and all my game characters are related to a common ancestor who is capable of using an AI method to manipulate a virtual joypad. I can do fun stuff like making all game characters playable this way, and it makes the code more readable and consitent, I think.
|
|
|
Logged
|
|
|
|
Pages: [1]
|
|
|
|
|