# GMLscripts.com

Discuss and collaborate on GML scripts

You are not logged in.

## #1 2013-12-23 02:57:11

Nikaple
Member
Registered: 2013-12-23
Posts: 7

### Drawing one view in different places ?

I'm using a GM8 pro version and want to create a cool effect that can draw a lot of copies of one view simultaneously.
GM provides 8 views but I'm not satisfied with that (maybe 16 or more).
so I want to use a surface to solve that problem. Here's what I got so far(only 4 copies):
Create Event:

Expandglobal.surf=surface_create(room_width,room_height);

End Step Event:

Expandif(!surface_exists(global.surf)){
global.surf=surface_create(room_width,room_height);
}
surface_set_target(global.surf);
draw_clear_alpha(c_black,1);
screen_redraw(); //capture the screen
surface_reset_target();

Draw Event:

Expandif(surface_exists(global.surf)){
draw_surface_ext(global.surf,0,0,0.5,0.5,0,c_white,1)
draw_surface_ext(global.surf,room_width/2,0,0.5,0.5,0,c_white,1)
draw_surface_ext(global.surf,0,room_height/2,0.5,0.5,0,c_white,1)
draw_surface_ext(global.surf,room_width/2,room_height/2,0.5,0.5,0,c_white,1)
//this can be changed to the draw_surface_tiled_ext function to adapt for more
}

and my views become messy. So I know probably I shouldn't draw on the screen anymore to avoid the repeating capturing the screen,
but if I stop drawing on the screen, screen_redraw() can't capture my screen anymore. I'm in such a paradox and don't know what to do..

I'm not very good in English , hope that you can understand me

Any help will be appreciated!

Offline

## #2 2013-12-24 06:09:01

xot
Registered: 2007-08-18
Posts: 1,202

### Re: Drawing one view in different places ?

If you don't want to have your draw event interfering with your call to screen_redraw(), you can make the object invisible before calling it.

Expandsurface_set_target(global.surf);
draw_clear_alpha(c_black,1);
visible = false;
screen_redraw(); //capture the screen
visible = true;
surface_reset_target();

However, I'm not sure I understand the question.

There are also ways to capture a screen to a surface without using screen_redraw(). That might work better for you.

This tutorial by ChevyRay explains one method I've had success with:
http://forums.tigsource.com/index.php?t … 9#msg86809

The important parts are:
1. In the End Step event, use surface_set_target().
2. In the Begin Step event, use surface_reset_target(), draw the surface to the display, and use screen_refresh() to update the window.

A similar method from YellowAfterlife is demonstrated here:
http://yal.cc/gamemaker-automatic-pixel … w-scaling/

Abusing forum power since 1986.

Offline

## #3 2013-12-25 00:40:55

Nikaple
Member
Registered: 2013-12-23
Posts: 7

### Re: Drawing one view in different places ?

xot wrote:

If you don't want to have your draw event interfering with your call to screen_redraw(), you can make the object invisible before calling it.

Expandsurface_set_target(global.surf);
draw_clear_alpha(c_black,1);
visible = false;
screen_redraw(); //capture the screen
visible = true;
surface_reset_target();

However, I'm not sure I understand the question.

There are also ways to capture a screen to a surface without using screen_redraw(). That might work better for you.

This tutorial by ChevyRay explains one method I've had success with:
http://forums.tigsource.com/index.php?t … 9#msg86809

The important parts are:
1. In the End Step event, use surface_set_target().
2. In the Begin Step event, use surface_reset_target(), draw the surface to the display, and use screen_refresh() to update the window.

A similar method from YellowAfterlife is demonstrated here:
http://yal.cc/gamemaker-automatic-pixel … w-scaling/

thx for the solution ! I'll try it first

the effect I want to achieve is like this.

one on the left is what actually happens on the screen

the right one is what shows on the screen .

Anyway , I'll try your method first

EDIT: maybe you didn't understand me , and my code is really messy.

what I want to do is draw the whole screen on one surface, and use draw_surface_tiled_ext to fill that surface to the whole screen.

but I don't know how to disable the drawing on screen, without interfering other objects.

Last edited by Nikaple (2013-12-25 01:11:59)

Offline

## #4 2013-12-25 10:49:43

xot
Registered: 2007-08-18
Posts: 1,202

### Re: Drawing one view in different places ?

This is how I would do it, using a method like ChevyRay's. It is efficient and the object can be inserted into any project without changing anything else.

https://www.dropbox.com/s/ygl0k19ejbksd … e.gmk?dl=0

Abusing forum power since 1986.

Offline

## #5 2013-12-25 11:04:05

Nikaple
Member
Registered: 2013-12-23
Posts: 7

### Re: Drawing one view in different places ?

xot wrote:

This is how I would do it, using a method like ChevyRay's. It is efficient and the object can be inserted into any project without changing anything else.

https://www.dropbox.com/s/ygl0k19ejbksd … e.gmk?dl=0

this is amazing !

maybe what I'm doing wrong is drawing the surface in the draw event , not in the Begin Step event .. thanks xot!

Offline

## #6 2013-12-25 12:06:50

xot
Registered: 2007-08-18
Posts: 1,202

### Re: Drawing one view in different places ?

You can draw the surface in the draw event but you need to make sure it is not drawn when you call screen_redraw(). You could use a variable to control that or use instance visibility like I showed earlier. That's a method I've used in the past.

However, the way I do it in the example is less complicated, faster, and generally works well in GM8. This method would not work with GameMaker: Studio.

Abusing forum power since 1986.

Offline

## #7 2014-02-28 12:33:18

Nikaple
Member
Registered: 2013-12-23
Posts: 7

### Re: Drawing one view in different places ?

xot wrote:

You can draw the surface in the draw event but you need to make sure it is not drawn when you call screen_redraw(). You could use a variable to control that or use instance visibility like I showed earlier. That's a method I've used in the past.

However, the way I do it in the example is less complicated, faster, and generally works well in GM8. This method would not work with GameMaker: Studio.

can I draw the screen in a black & white style ?
I saw there was a script called sprite_desaturate to desaturate the sprite, it worked out well for sprites. But when I want to desaturate all the instances on the screen, it became really slow to process. So I wonder if I can desaturate the screen instead of desaturate the separate sprites. I tried to play with the code in sprite_desaturate in order to desaturate the screen, but it didn't turn out well (honestly I didn't understand the method in that script ). I revised some of the code in the Begin Step Event:

Expandif (surface_exists(surfScreen)){
surface_set_target(surfScreen);
draw_clear_alpha(c_white,1);
tempsprite = sprite_create_from_surface(surfScreen,0,0,view_wview[0],view_hview[0],0,0,0,0);
draw_clear_alpha(c_black,1);
draw_sprite(tempsprite,i,0,0);
draw_rectangle_color(0,0,view_wview[0],view_hview[0],c_black,c_black,c_black,c_black,false);
newsprite = sprite_create_from_surface(surfScreen,0,0,view_wview[0],view_hview[0],0,0,0,0);
draw_sprite(newsprite,-1,0,0);
surface_reset_target();
draw_surface(surfScreen,0,0);
//    screen_refresh();
}
else{
surfScreen = surface_create(view_wview[0], view_hview[0]);
}

but sadly I got nothing. Could you help me with it?

Last edited by Nikaple (2014-02-28 12:33:51)

Offline

## #8 2014-03-03 18:35:24

xot
Registered: 2007-08-18
Posts: 1,202

### Re: Drawing one view in different places ?

The idea behind the method is to use the original sprite's brightness as an alpha channel for a new solid white sprite. This is done with a call to sprite_set_alpha_from_sprite(). The bright areas from the original sprite make the respective areas of the new white sprite opaque. Likewise, the dark areas make the respective areas of the new white sprite transparent. When this semitransparent white sprite is drawn on top of black, it results in a grayscale image.

The only way to manipulate alpha channels like this is to use sprite_set_alpha_from_sprite(). Color/alpha information cannot otherwise be communicated between channels, using blending mode for instance. Unfortunately, this requires the creation of sprites and creating large sprites is quite slow. Doing this every step is generally impractical unless you are working in a very low resolution, but it is worth a try. Hopefully, I've given you enough info to help you adjust your code.

There are some other options for GM8. One is maintain duplicate B&W sprites and switch between them and the normal sprites as needed. They could be generated once at the start of the game and used thereafter.

Another option is shaders. Shaders can do this sort of effect quite efficiently. If you don't mind using a GEX/DLL, ps1.4 shaders can be used in GM8. Unfortunately, it does not work with GM8.1. Also, the shaders must be written in assembly, not HLSL. I have some experience with this and can help you.

http://gmc.yoyogames.com/index.php?showtopic=492876

Abusing forum power since 1986.

Offline

## #9 2014-03-06 03:08:17

Nikaple
Member
Registered: 2013-12-23
Posts: 7

### Re: Drawing one view in different places ?

xot wrote:

The idea behind the method is to use the original sprite's brightness as an alpha channel for a new solid white sprite. This is done with a call to sprite_set_alpha_from_sprite(). The bright areas from the original sprite make the respective areas of the new white sprite opaque. Likewise, the dark areas make the respective areas of the new white sprite transparent. When this semitransparent white sprite is drawn on top of black, it results in a grayscale image.

The only way to manipulate alpha channels like this is to use sprite_set_alpha_from_sprite(). Color/alpha information cannot otherwise be communicated between channels, using blending mode for instance. Unfortunately, this requires the creation of sprites and creating large sprites is quite slow. Doing this every step is generally impractical unless you are working in a very low resolution, but it is worth a try. Hopefully, I've given you enough info to help you adjust your code.

There are some other options for GM8. One is maintain duplicate B&W sprites and switch between them and the normal sprites as needed. They could be generated once at the start of the game and used thereafter.

Another option is shaders. Shaders can do this sort of effect quite efficiently. If you don't mind using a GEX/DLL, ps1.4 shaders can be used in GM8. Unfortunately, it does not work with GM8.1. Also, the shaders must be written in assembly, not HLSL. I have some experience with this and can help you.

http://gmc.yoyogames.com/index.php?showtopic=492876

If I took the first method, how can I cycle through all the sprites in my game ? And if I want to switch between the sprites, the method I came up with is using execute_string, will that slow the game down too?

Offline

## #10 2014-03-06 10:35:28

xot
Registered: 2007-08-18
Posts: 1,202

### Re: Drawing one view in different places ?

Nikaple wrote:

If I took the first method, how can I cycle through all the sprites in my game ? And if I want to switch between the sprites, the method I came up with is using execute_string, will that slow the game down too?

Both problems can be addressed with this resource management script:

http://www.gmlscripts.com/script/map_sprites

That will populate a ds_map with sprite indices keyed by strings of their names. This way you can construct a string that matches a resources name and retrieve its index without resorting to execute_string(). I'm guessing you were doing something like that. You may not need to do that with method given below.

At the start of your game:

Expand{
global.mapSprites = ds_map_create();
global.mapSpritesBW = ds_map_create();
global.mapSpritesColor = ds_map_create();

// Populate a map with every sprite resource in the game
map_sprites(global.mapSprites);

var key, val, sprite;

// Find the key of the first sprite
key = ds_map_find_first(global.mapSprites);

// For each sprite in the map ...
repeat (ds_map_size(global.mapSprites))
{
// Find the index of a sprite resource
val = ds_map_find_value(global.mapSprites, key);

// Create a duplicate and desaturate it
sprite = sprite_duplicate(val);
sprite_desaturate(sprite);

// Add the desaturated sprite to a map,
// keying it by the index of the original sprite

// Add the original sprite to a map,
// keying it by the index of the duplicate sprite

// Find the key to the next sprite
key = ds_map_find_next(global.mapSprites, key);
}
}

Now when you want to draw a desaturated sprite you have a couple of options.

Option 1: Draw it directly

Expand{
// Draw the desaturated version of the current sprite
draw_sprite(ds_map_find_value(global.mapSpritesBW, sprite_index), -1, x, y);
}

Option 2a: Change the sprite of the instance

Expand{
// Change the current sprite to the alternate version
if (bw)
{
// Switch from desaturated to original
bw = false;
sprite_index = ds_map_find_value(global.mapSpritesColor, sprite_index);
}
else
{
// Switch from original to desaturated
bw = true;
sprite_index = ds_map_find_value(global.mapSpritesBW, sprite_index);
}
}

Option 2b: Change the sprite of the instance (same as above but stateless)

Expand{
// Change the current sprite to its alternate version, if any
if (ds_map_exists(global.mapSpritesColor, sprite_index))
{
sprite_index = ds_map_find_value(global.mapSpritesColor, sprite_index);
}
else if (ds_map_exists(global.mapSpritesBW, sprite_index))
{
sprite_index = ds_map_find_value(global.mapSpritesBW, sprite_index);
}
}

And if for some reason you need to get the index of a resource based on a string of its name.

Expand{
sprite_index = ds_map_find_value(global.mapSprites, "sprite0");
}

One final thing: For GM8 the map_sprites() script needs a small change due to differences in the way sprites are created.

Expand/*
**  Usage:
**      map_sprites(ds_map)
**
**  Arguments:
**      ds_map      map to which all sprites are loaded
**
**  Returns:
**      nothing, but fills the map with {key=name, val=index} pairs
**
**  GMLscripts.com
*/
{
var no,i,ds_map;
ds_map = argument0;
no = sprite_create_from_screen(0,0,1,1,false,false,0,0);
sprite_delete(no);
for (i=0; i<no; i+=1) {
if (sprite_exists(i)) {
}
}
}

Abusing forum power since 1986.

Offline

## #11 2014-03-18 12:10:13

Nikaple
Member
Registered: 2013-12-23
Posts: 7

### Re: Drawing one view in different places ?

xot wrote:

Both problems can be addressed with this resource management script:

http://www.gmlscripts.com/script/map_sprites

...

this method worked out very well, but when I'm using this I found a problem:
in a keyboard press event I use the code:

Expandwith (all){
// Change the current sprite to its alternate version, if any
if (ds_map_exists(global.mapSpritesColor, sprite_index))
{
sprite_index = ds_map_find_value(global.mapSpritesColor, sprite_index);
}
else if (ds_map_exists(global.mapSpritesBW, sprite_index))
{
sprite_index = ds_map_find_value(global.mapSpritesBW, sprite_index);
}
}

but it came out that all of my objects are having a square collision box. It seems that when I'm desaturating the sprites, I also need to copy the original mask information to the new b&w sprites? One method I found is to set every object's mask in the object properties independently, but that's a little complicated and I need some simpler scripts.
I tried this piece of code but it didn't work out:

Expandif mask_index == -1
{
}

so I guess I totally lost my mask data after sprite_desaturate, is there a method to maintain the mask? Thanks in advance !

Offline

## #12 2014-03-18 13:52:37

xot
Registered: 2007-08-18
Posts: 1,202

### Re: Drawing one view in different places ?

Oh, I see. GM8 significantly changed the way sprite collisions are defined.

In GM7 it is possible to copy the collision settings of the original sprite, which is what sprite_desaturate() does for that version. All that is needed is to check if precise collisions are enabled using sprite_get_precise() and use the same setting when creating the new sprite.

In GM8 this is not possible because sprite_get_precise() has been removed. There are more collision options than just "precise", 8 in all I think. These can be set with sprite_collision_mask() but there is no way to retrieve this information from the original sprite.

I think you have the right solution but maybe your code wasn't in the right place. This should work:

Expandwith (all){
// Change the current sprite to its alternate version, if any
if (ds_map_exists(global.mapSpritesColor, sprite_index))
{
sprite_index = ds_map_find_value(global.mapSpritesColor, sprite_index);
}
else if (ds_map_exists(global.mapSpritesBW, sprite_index))
{
if (mask_index == -1) mask_index = sprite_index;  // <-- THE CHANGE IS HERE
sprite_index = ds_map_find_value(global.mapSpritesBW, sprite_index);
}
}

I tried a more complicated solution of using code to change the mask of every object, similar to the way every sprite is desaturated. It did not work. It seems that GM does some silly optimization when an instance is created: it changes the mask_index to -1 if it matches the sprite_index. It does not seem to do this if the mask is set manually in the editor, however. The object retains the changed mask in either case. It's weird.

Abusing forum power since 1986.

Offline

## #13 2014-03-19 23:22:48

Nikaple
Member
Registered: 2013-12-23
Posts: 7

### Re: Drawing one view in different places ?

All right. Problem solved! thx:)

Offline