以下是翻译结果:

Feypath的光照引擎

这是一个短视频,展示了Feypath的光照引擎。这个引擎使用一个自定义的方法,已经在我的基于砖块的游戏中得到了很好的效果。如果你感兴趣,可以查看我的光照对象的代码(请自由问任何问题):

debug_m = true
tile_size = 16
light_radius = 6
level_width = 16
level_height = 14 
tiles_processed = 0
rects_created = 0
verts_drawn = 0


shadow_surface = surface_create(256, 224)
function init_shadow_surface(){
    if not surface_exists(shadow_surface) shadow_surface = surface_create(256, 224)
    surface_set_target(shadow_surface)
    draw_clear_alpha(c_black, 1)
}


global.map = layer_tilemap_get_id("Tiles")
//Declare level data array
tile_data = array_create(level_width)
for (var _column = 0; _column < level_width; _column++) {
    tile_data[_column] = array_create(level_height)
}

//Populate level data array
for (var _column = 0 ; _column < level_width ; _column ++){
    for (var _row = 0 ; _row < level_height ; _row ++){
        tile_data[_column][_row] = tilemap_get(global.map, _column, _row) != 0
    }
}

function get_tile_at_pixel(_pixel_x,_pixel_y){
    return tile_data[floor(_pixel_x / tile_size)][floor(_pixel_y / tile_size)]
}

function generate_skip_tiles_array(){
    var _skip_tiles = array_create(level_width)
    for (var _column = 0; _column < level_width; _column++) {
        _skip_tiles[_column] = array_create(level_height)
    }
    return _skip_tiles
}

//DRAWING STUFF

function draw_rectangles(){

    verts_drawn = 0
    tiles_processed = 0
    rects_created = 0
    var _start_column = max(0, floor(x / tile_size) - light_radius)
    var _end_column = min(level_width, floor(x / tile_size) + light_radius)
    var _start_row = max(0, floor(y / tile_size) - light_radius)
    var _end_row = min(level_height, floor(y / tile_size) + light_radius)

    var _skip_tiles = generate_skip_tiles_array()

    init_shadow_surface()

    draw_set_color(c_white)

    draw_circle_colour(x, y , light_radius * tile_size, c_white, c_black, 0)
    for (var _column = _start_column ; _column < _end_column; _column ++){
        for (var _row = _start_row ; _row < _end_row; _row ++){

            if tile_data[_column][_row] tiles_processed ++

            //Check if we can start the rectangle, the rectangle can be started if the tile is solid and it does not exist in _skip_tiles
            if tile_data[_column][_row] and not _skip_tiles[_column][_row]{

                rects_created ++
                var _rectangle_width = 1
                var _rectangle_height = 1

                //checking how wide our rectangle will be
                var _right_border_reached = false
                while not _right_border_reached{
                    var _next_column = _column + _rectangle_width
                    if _next_column >= _end_column _right_border_reached = true
                    else if (not tile_data[_next_column][_row]) or _skip_tiles[_next_column][_row] _right_border_reached = true
                    else {
                        _rectangle_width ++
                        _skip_tiles[_next_column][_row] = true
                    }

                }
                //using the info about rectangle width, we check how many rows we can add (if any)
                var _failed_to_add_row = false
                var _next_row = _row + 1
                var _next_column = _column
                while not _failed_to_add_row{


                    if _next_row >= _end_row  _failed_to_add_row = true
                    else if (not tile_data[_next_column][_next_row]) or _skip_tiles[_next_column][_next_row]{
                        _failed_to_add_row = true

                    }
                    else{

                        _next_column ++
                        if _next_column == _column + _rectangle_width{

                            for (var _skip_col = _column; _skip_col < _column + _rectangle_width; _skip_col ++){
                                _skip_tiles[_skip_col][_next_row] = true
                            }
                            _rectangle_height ++
                            _next_row ++
                            _next_column = _column
                        }
                    }

                }
                //draw the rectangle DEBUG ONLY


                if debug_m {
                    draw_set_color(c_white)
                    draw_rectangle(_column * tile_size, _row * tile_size, (_column + _rectangle_width) * tile_size,(_row + _rectangle_height) * tile_size, 1)
                }
                //Find the two corners
                var _left_x = _column * tile_size
                var _upper_y = _row * tile_size 
                var _right_x = (_column + _rectangle_width) * tile_size
                var _bottom_y =(_row + _rectangle_height) * tile_size 
                var _corner_pairs = [
                    [_left_x, _upper_y, _right_x, _upper_y],
                    [_left_x, _upper_y, _right_x, _bottom_y],
                    [_left_x, _upper_y, _left_x, _bottom_y],

                    [_left_x, _bottom_y, _right_x, _bottom_y],
                    [_right_x, _upper_y, _left_x, _bottom_y],
                    [_right_x, _upper_y, _right_x, _bottom_y]
                ]

                var _largest_angle = 0
                var _largest_pair = 0
                for (var _corner_pair = 0; _corner_pair < 6; _corner_pair ++){
                    var _x1 = _corner_pairs[_corner_pair][0]
                    var _y1 = _corner_pairs[_corner_pair][1]
                    var _x2 = _corner_pairs[_corner_pair][2]
                    var _y2 = _corner_pairs[_corner_pair][3]

                    var _ang1 = point_direction(x, y, _x1, _y1);
                    var _ang2 = point_direction(x, y, _x2, _y2);

                    var _current_angle = abs(angle_difference(_ang1,_ang2))

                    if _current_angle > _largest_angle{
                        _largest_angle = _current_angle
                        _largest_pair = _corner_pair
                    }
                }

                //The "opposing pair" are the corners currently not present in the largest pair - we need to use this in a bit if the largest pair form a diagonal in the rectangle
                var _opposing_pair = (_largest_pair + 3) mod 6

                // make the corner points more readable
                var _point_1_x = _corner_pairs[_largest_pair][0]
                var _point_1_y = _corner_pairs[_largest_pair][1]
                var _point_2_x = _corner_pairs[_largest_pair][2]
                var _point_2_y = _corner_pairs[_largest_pair][3]

                //Find the extension points

                var _angle_1 = point_direction(x, y, _point_1_x, _point_1_y)
                var _angle_2 = point_direction(x, y, _point_2_x, _point_2_y)
                var _middle_angle = _angle_1 + angle_difference(_angle_2, _angle_1) * 0.5

                //nudge the outer angles outward to remove shadow gaps

                var _nudge_amount = 2

                //we only do the nudge if the angle is either very close to horizontal or vertical. It is in those cases we might run into gaps
                if abs(cos(degtorad(_angle_1))) < 0.02 or abs(sin(degtorad(_angle_1))) < 0.02{
                    if _angle_1 > _middle_angle _angle_1 += _nudge_amount else _angle_1 -= _nudge_amount
                }
                if abs(cos(degtorad(_angle_2))) < 0.02 or abs(sin(degtorad(_angle_2))) < 0.02{
                    if _angle_2 > _middle_angle _angle_2 += _nudge_amount else _angle_2 -= _nudge_amount
                }
                //end of nudge

                var _ext_point_1_x = x + cos(degtorad(_angle_1)) * light_radius * 2 * tile_size//We multiply by two to make sure our wedge properly covers the shadowed area
                var _ext_point_1_y = y - sin(degtorad(_angle_1)) * light_radius * 2 * tile_size

                var _ext_point_2_x = x + cos(degtorad(_angle_2)) * light_radius * 2 * tile_size 
                var _ext_point_2_y = y - sin(degtorad(_angle_2)) * light_radius * 2 * tile_size

                var _ext_point_3_x = x + cos(degtorad(_middle_angle)) * light_radius * 2 * tile_size
                var _ext_point_3_y = y - sin(degtorad(_middle_angle)) * light_radius * 2 * tile_size

                //Draw the triangles
                if debug_m {
                    draw_set_color(c_blue)

                    draw_circle(_corner_pairs[_largest_pair][0],_corner_pairs[_largest_pair][1], 3, 1)
                    draw_circle(_corner_pairs[_largest_pair][2],_corner_pairs[_largest_pair][3], 3, 1)

                    draw_set_color(c_green)

                    draw_circle(_ext_point_1_x,_ext_point_1_y, 3, 1)
                    draw_circle(_ext_point_2_x,_ext_point_2_y, 3, 1)
                    draw_circle(_ext_point_3_x,_ext_point_3_y, 3, 1)

                    draw_set_color(c_red)
                    if _draw_closest_corner draw_circle(_closest_corner_x, _closest_corner_y, 3, 1)

                    draw_set_color(c_yellow)
                    draw_line(_corner_pairs[_largest_pair][0],_corner_pairs[_largest_pair][1],_ext_point_1_x,_ext_point_1_y)
                    draw_line(_corner_pairs[_largest_pair][2],_corner_pairs[_largest_pair][3],_ext_point_2_x,_ext_point_2_y)
                    draw_line(_ext_point_1_x,_ext_point_1_y, _ext_point_3_x, _ext_point_3_y)
                    draw_line(_ext_point_3_x, _ext_point_3_y,_ext_point_2_x,_ext_point_2_y)

                }

                //The "opposing pair" are the corners currently not present in the largest pair - we need to use this in a bit if the largest pair form a diagonal in the rectangle
                var _opposing_pair = (_largest_pair + 3) mod 6

                // make the corner points more readable
                var _point_1_x = _corner_pairs[_largest_pair][0]
                var _point_1_y = _corner_pairs[_largest_pair][1]
                var _point_2_x = _corner_pairs[_largest_pair][2]
                var _point_2_y = _corner_pairs[_largest_pair][3]

                //Find the extension points

                var _angle_1 = point_direction(x, y, _point_1_x, _point_1_y)
                var _angle_2 = point_direction(x, y, _point_2_x, _point_2_y)
                var _middle_angle = _angle_1 + angle_difference(_angle_2, _angle_1) * 0.5

                //nudge the outer angles outward to remove shadow gaps

                var _nudge_amount = 2

                //we only do the nudge if the angle is either very close to horizontal or vertical. It is in those cases we might run into gaps
                if abs(cos(degtorad(_angle_1))) < 0.02 or abs(sin(degtorad(_angle_1))) < 0.02{
                    if _angle_1 > _middle_angle _angle_1 += _nudge_amount else _angle_1 -= _nudge_amount
                }
                if abs(cos(degtorad(_angle_2))) < 0.02 or abs(sin(degtorad(_angle_2))) < 0.02{
                    if _angle_2 > _middle_angle _angle_2 += _nudge_amount else _angle_2 -= _nudge_amount
                }
                //end of nudge

                var _ext_point_1_x = x + cos(degtorad(_angle_1)) * light_radius * 2 * tile_size//We multiply by two to make sure our wedge properly covers the shadowed area
                var _ext_point_1_y = y - sin(degtorad(_angle_1)) * light_radius * 2 * tile_size

                var _ext_point_2_x = x + cos(degtorad(_angle_2)) * light_radius * 2 * tile_size 
                var _ext_point_2_y = y - sin(degtorad(_angle_2)) * light_radius * 2 * tile_size

                var _ext_point_3_x = x + cos(degtorad(_middle_angle)) * light_radius * 2 * tile_size
                var _ext_point_3_y = y - sin(degtorad(_middle_angle)) * light_radius * 2 * tile_size

                //Draw the triangles
                draw_set_color(c_black)
                if debug_m draw_set_alpha(0.2)
                draw_primitive_begin(pr_trianglestrip)

                if _draw_closest_corner{
                    draw_vertex(_closest_corner_x,_closest_corner_y )
                    verts_drawn ++
                }
                draw_vertex(_point_1_x,_point_1_y)

                draw_vertex(_point_2_x,_point_2_y )
                draw_vertex(_ext_point_1_x ,_ext_point_1_y)
                draw_vertex(_ext_point_2_x ,_ext_point_2_y)
                draw_vertex(_ext_point_3_x ,_ext_point_3_y)
                verts_drawn += 5 

                draw_primitive_end()
                if debug_m draw_set_alpha(1)
            }
        }
    }
    surface_reset_target()

    if not debug_m shader_set(sh_360_light)

    draw_surface(shadow_surface,floor(x / 256) * 256,floor(y / 224) * 224)
    if not debug_m shader_reset()

}

注意: 该代码是基于GameMaker Studio 2的脚本语言。