import retro platformer controller and mod player
This commit is contained in:
parent
992bdac15e
commit
40d2372f2b
|
@ -0,0 +1,23 @@
|
||||||
|
"""
|
||||||
|
Godot Mod Player Plugin by arlez80 (Yui Kinomoto)
|
||||||
|
"""
|
||||||
|
|
||||||
|
tool
|
||||||
|
extends EditorPlugin
|
||||||
|
|
||||||
|
func _enter_tree( ):
|
||||||
|
#self.add_custom_type( "GodotModPlayer", "Spatial", preload("ModPlayer.gd"), preload("icon.png") )
|
||||||
|
pass
|
||||||
|
|
||||||
|
func _exit_tree( ):
|
||||||
|
#self.remove_custom_type( "GodotModPlayer" )
|
||||||
|
pass
|
||||||
|
|
||||||
|
func has_main_screen():
|
||||||
|
return true
|
||||||
|
|
||||||
|
func make_visible( visible:bool ):
|
||||||
|
pass
|
||||||
|
|
||||||
|
func get_plugin_name( ):
|
||||||
|
return "Godot Mod Player"
|
|
@ -0,0 +1,290 @@
|
||||||
|
"""
|
||||||
|
MOD reader by あるる(きのもと 結衣) @arlez80
|
||||||
|
|
||||||
|
MIT License
|
||||||
|
"""
|
||||||
|
|
||||||
|
class_name Mod
|
||||||
|
|
||||||
|
const period_table:PoolIntArray = PoolIntArray([
|
||||||
|
1712,1616,1525,1440,1357,1281,1209,1141,1077,1017, 961, 907,
|
||||||
|
856, 808, 762, 720, 678, 640, 604, 570, 538, 508, 480, 453,
|
||||||
|
428, 404, 381, 360, 339, 320, 302, 285, 269, 254, 240, 226,
|
||||||
|
214, 202, 190, 180, 170, 160, 151, 143, 135, 127, 120, 113,
|
||||||
|
107, 101, 95, 90, 85, 80, 76, 71, 67, 64, 60, 57,
|
||||||
|
])
|
||||||
|
|
||||||
|
enum ModFlags {
|
||||||
|
LINEAR_FREQUENCY_TABLE = 1,
|
||||||
|
}
|
||||||
|
|
||||||
|
enum ModWaveFormType {
|
||||||
|
SINE_WAVEFORM = 0,
|
||||||
|
SAW_WAVEFORM = 1,
|
||||||
|
SQUARE_WAVEFORM = 2,
|
||||||
|
RAMDOM_WAVEFORM = 3,
|
||||||
|
REV_SAW_WAVEFORM = 4,
|
||||||
|
}
|
||||||
|
|
||||||
|
enum ModLoopType {
|
||||||
|
NO_LOOP = 0,
|
||||||
|
FORWARD_LOOP = 1,
|
||||||
|
PING_PONG_LOOP = 2,
|
||||||
|
}
|
||||||
|
|
||||||
|
class ModData:
|
||||||
|
var module_name:String
|
||||||
|
var tracker_name:String
|
||||||
|
var version:int
|
||||||
|
var flags:int = 0
|
||||||
|
|
||||||
|
var song_length:int
|
||||||
|
var unknown_number:int
|
||||||
|
var channel_count:int
|
||||||
|
var song_positions:Array # int
|
||||||
|
var magic:String
|
||||||
|
var patterns:Array # ModPattern[][]
|
||||||
|
var instruments:Array # ModInstrument[]
|
||||||
|
|
||||||
|
var init_tick:int = 6
|
||||||
|
var init_bpm:int = 125
|
||||||
|
var restart_position:int = 0
|
||||||
|
|
||||||
|
class ModPatternNote:
|
||||||
|
var key_number:int
|
||||||
|
var note:int
|
||||||
|
var instrument:int
|
||||||
|
var volume:int = 0
|
||||||
|
var effect_command:int
|
||||||
|
var effect_param:int
|
||||||
|
|
||||||
|
class ModInstrument:
|
||||||
|
var name:String
|
||||||
|
var samples:Array # ModSample[96]
|
||||||
|
var volume_envelope:ModEnvelope
|
||||||
|
var panning_envelope:ModEnvelope
|
||||||
|
var vibrato_type:int = -1 # ModWaveFormType
|
||||||
|
var vibrato_speed:int = -1
|
||||||
|
var vibrato_depth:int = -1
|
||||||
|
var vibrato_depth_shift:int = -1
|
||||||
|
var volume_fadeout:int = -1
|
||||||
|
|
||||||
|
class ModSample:
|
||||||
|
var data:PoolByteArray
|
||||||
|
var name:String
|
||||||
|
var length:int
|
||||||
|
var loop_start:int
|
||||||
|
var loop_length:int
|
||||||
|
var volume:int
|
||||||
|
var finetune:int
|
||||||
|
var loop_type:int = ModLoopType.FORWARD_LOOP # set(ModLoopType)
|
||||||
|
var bit:int = 8 # 8 or 16
|
||||||
|
var panning:int = -1
|
||||||
|
var relative_note:int = 0
|
||||||
|
|
||||||
|
class ModEnvelope:
|
||||||
|
var points:Array # ModEnvelopePoint[12]
|
||||||
|
var point_count:int
|
||||||
|
var sustain_point:int
|
||||||
|
var loop_start_point:int
|
||||||
|
var loop_end_point:int
|
||||||
|
|
||||||
|
var enabled:bool
|
||||||
|
var sustain_enabled:bool
|
||||||
|
var loop_enabled:bool
|
||||||
|
|
||||||
|
func set_flag( f:int ):
|
||||||
|
self.enabled = ( f & 1 ) != 0
|
||||||
|
self.sustain_enabled = ( f & 2 ) != 0
|
||||||
|
self.loop_enabled = ( f & 4 ) != 0
|
||||||
|
|
||||||
|
class ModEnvelopePoint:
|
||||||
|
var frame:int
|
||||||
|
var value:int
|
||||||
|
|
||||||
|
class ModParseResult:
|
||||||
|
var error:int = OK
|
||||||
|
var data:ModData = null
|
||||||
|
|
||||||
|
func _init( ):
|
||||||
|
pass
|
||||||
|
|
||||||
|
"""
|
||||||
|
ファイルから読み込み
|
||||||
|
|
||||||
|
@param path ファイルパス
|
||||||
|
@return 読んだ Modファイルデータ
|
||||||
|
"""
|
||||||
|
func read_file( path:String ) -> ModParseResult:
|
||||||
|
var f:File = File.new( )
|
||||||
|
var result: = ModParseResult.new( )
|
||||||
|
|
||||||
|
var err:int = f.open( path, f.READ )
|
||||||
|
if err != OK:
|
||||||
|
result.error = err
|
||||||
|
return result
|
||||||
|
|
||||||
|
var stream:StreamPeerBuffer = StreamPeerBuffer.new( )
|
||||||
|
stream.set_data_array( f.get_buffer( f.get_len( ) ) )
|
||||||
|
stream.big_endian = true
|
||||||
|
f.close( )
|
||||||
|
|
||||||
|
result.data = self._read( stream )
|
||||||
|
return result
|
||||||
|
|
||||||
|
"""
|
||||||
|
配列から読み込み
|
||||||
|
|
||||||
|
@param data データ
|
||||||
|
@return ModData
|
||||||
|
"""
|
||||||
|
func read_data( data:PoolByteArray ) -> ModParseResult:
|
||||||
|
var stream:StreamPeerBuffer = StreamPeerBuffer.new( )
|
||||||
|
stream.set_data_array( data )
|
||||||
|
stream.big_endian = true
|
||||||
|
|
||||||
|
var result: = ModParseResult.new( )
|
||||||
|
result.data = self._read( stream )
|
||||||
|
return result
|
||||||
|
|
||||||
|
"""
|
||||||
|
ストリームから読み込み
|
||||||
|
|
||||||
|
@param stream ストリーム
|
||||||
|
@return 読んだ Modファイルデータ
|
||||||
|
"""
|
||||||
|
func _read( stream:StreamPeerBuffer ) -> ModData:
|
||||||
|
var mod: = ModData.new( )
|
||||||
|
mod.module_name = self._read_string( stream, 20 )
|
||||||
|
mod.tracker_name = ""
|
||||||
|
mod.instruments = self._read_instruments( stream )
|
||||||
|
mod.song_length = stream.get_u8( )
|
||||||
|
mod.unknown_number = stream.get_u8( )
|
||||||
|
mod.song_positions = stream.get_partial_data( 128 )[1]
|
||||||
|
var max_song_position:int = 0
|
||||||
|
for sp in mod.song_positions:
|
||||||
|
if max_song_position < sp:
|
||||||
|
max_song_position = sp
|
||||||
|
|
||||||
|
mod.magic = self._read_string( stream, 4 )
|
||||||
|
var channel_count:int = 4
|
||||||
|
match mod.magic:
|
||||||
|
"6CHN":
|
||||||
|
channel_count = 6
|
||||||
|
"FLT8", "8CHN", "CD81", "OKTA":
|
||||||
|
channel_count = 8
|
||||||
|
"16CN":
|
||||||
|
channel_count = 16
|
||||||
|
"32CN":
|
||||||
|
channel_count = 32
|
||||||
|
_:
|
||||||
|
if mod.magic.substr( 2 ) == "CH":
|
||||||
|
channel_count = int( mod.magic )
|
||||||
|
# print( "Unknown magic" )
|
||||||
|
# breakpoint
|
||||||
|
pass
|
||||||
|
mod.channel_count = channel_count
|
||||||
|
|
||||||
|
mod.patterns = self._read_patterns( stream, max_song_position + 1, channel_count )
|
||||||
|
self._read_sample_data( stream, mod.instruments )
|
||||||
|
|
||||||
|
return mod
|
||||||
|
|
||||||
|
"""
|
||||||
|
楽器データを読み込む
|
||||||
|
|
||||||
|
@param stream ストリーム
|
||||||
|
@return 楽器データ
|
||||||
|
"""
|
||||||
|
func _read_instruments( stream:StreamPeerBuffer ) -> Array: # of ModSample
|
||||||
|
var instruments:Array = []
|
||||||
|
|
||||||
|
for i in range( 31 ):
|
||||||
|
var sample: = ModSample.new( )
|
||||||
|
sample.name = self._read_string( stream, 22 )
|
||||||
|
sample.length = stream.get_u16( ) * 2
|
||||||
|
var finetune:int = stream.get_u8( ) & 0x0F
|
||||||
|
if 0x08 < finetune:
|
||||||
|
finetune = finetune - 0x10
|
||||||
|
sample.finetune = finetune * 16
|
||||||
|
sample.volume = stream.get_u8( )
|
||||||
|
sample.loop_start = stream.get_u16( ) * 2
|
||||||
|
sample.loop_length = stream.get_u16( ) * 2
|
||||||
|
if sample.loop_length < 8:
|
||||||
|
sample.loop_type = ModLoopType.NO_LOOP
|
||||||
|
else:
|
||||||
|
sample.loop_type = ModLoopType.FORWARD_LOOP
|
||||||
|
|
||||||
|
var instrument: = ModInstrument.new( )
|
||||||
|
instrument.name = sample.name
|
||||||
|
instrument.samples = []
|
||||||
|
for k in range( 96 ):
|
||||||
|
instrument.samples.append( sample )
|
||||||
|
instrument.volume_envelope = ModEnvelope.new( )
|
||||||
|
instrument.volume_envelope.enabled = false
|
||||||
|
instrument.panning_envelope = ModEnvelope.new( )
|
||||||
|
instrument.panning_envelope.enabled = false
|
||||||
|
|
||||||
|
instruments.append( instrument )
|
||||||
|
|
||||||
|
return instruments
|
||||||
|
|
||||||
|
"""
|
||||||
|
パターンを読み込む
|
||||||
|
|
||||||
|
@param stream ストリームデータ
|
||||||
|
@param max_positions 最大パターン数
|
||||||
|
@param channels 最大チャンネル数
|
||||||
|
@return パターンデータ
|
||||||
|
"""
|
||||||
|
func _read_patterns( stream:StreamPeerBuffer, max_position:int, channels:int ) -> Array:
|
||||||
|
var patterns:Array = []
|
||||||
|
|
||||||
|
for position in range( 0, max_position ):
|
||||||
|
var pattern:Array = []
|
||||||
|
for i in range( 0, 64 ):
|
||||||
|
var line:Array = []
|
||||||
|
for ch in range( 0, channels ):
|
||||||
|
var v1:int = stream.get_u16( )
|
||||||
|
var v2:int = stream.get_u16( )
|
||||||
|
var mod_pattern_note: = ModPatternNote.new( )
|
||||||
|
mod_pattern_note.instrument = ( ( v1 >> 8 ) & 0xF0 ) | ( ( v2 >> 12 ) & 0x0F )
|
||||||
|
mod_pattern_note.key_number = v1 & 0x0FFF
|
||||||
|
mod_pattern_note.effect_command = ( v2 >> 8 ) & 0x0F
|
||||||
|
mod_pattern_note.effect_param = v2 & 0x0FF
|
||||||
|
# TODO: 遅かったら二分探索にでもしませんか
|
||||||
|
if 0 < mod_pattern_note.key_number:
|
||||||
|
mod_pattern_note.note = 23
|
||||||
|
for k in period_table:
|
||||||
|
mod_pattern_note.note += 1
|
||||||
|
if k <= mod_pattern_note.key_number:
|
||||||
|
break
|
||||||
|
else:
|
||||||
|
mod_pattern_note.note = 0
|
||||||
|
line.append( mod_pattern_note )
|
||||||
|
pattern.append( line )
|
||||||
|
patterns.append( pattern )
|
||||||
|
|
||||||
|
return patterns
|
||||||
|
|
||||||
|
"""
|
||||||
|
楽器のデータに基づき、波形を読み込む
|
||||||
|
|
||||||
|
@param stream ストリーム
|
||||||
|
@param instruments 波形を読み込む楽器データリスト
|
||||||
|
"""
|
||||||
|
func _read_sample_data( stream:StreamPeerBuffer, instruments:Array ) -> void:
|
||||||
|
for instrument in instruments:
|
||||||
|
var sample:Mod.ModSample = instrument.samples[0]
|
||||||
|
if 0 < sample.length:
|
||||||
|
sample.data = stream.get_partial_data( sample.length )[1]
|
||||||
|
|
||||||
|
"""
|
||||||
|
文字列の読み込み
|
||||||
|
|
||||||
|
@param stream ストリーム
|
||||||
|
@param size 文字列サイズ
|
||||||
|
@return 読み込んだ文字列を返す
|
||||||
|
"""
|
||||||
|
func _read_string( stream:StreamPeerBuffer, size:int ) -> String:
|
||||||
|
return stream.get_partial_data( size )[1].get_string_from_ascii( )
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,6 @@
|
||||||
|
[gd_scene load_steps=2 format=2]
|
||||||
|
|
||||||
|
[ext_resource path="res://addons/modplayer/ModPlayer.gd" type="Script" id=1]
|
||||||
|
|
||||||
|
[node name="ModPlayer" type="Node"]
|
||||||
|
script = ExtResource( 1 )
|
|
@ -0,0 +1,287 @@
|
||||||
|
"""
|
||||||
|
XM reader by あるる(きのもと 結衣) @arlez80
|
||||||
|
|
||||||
|
This script required Mod.gd for declaring the data structures.
|
||||||
|
|
||||||
|
MIT License
|
||||||
|
"""
|
||||||
|
|
||||||
|
class_name XM
|
||||||
|
|
||||||
|
enum XMNoteFlags {
|
||||||
|
NOTE = 0x01,
|
||||||
|
INSTRUMENT = 0x02,
|
||||||
|
VOLUME = 0x04,
|
||||||
|
EFFECT_COMMAND = 0x08,
|
||||||
|
EFFECT_PARAM = 0x10,
|
||||||
|
|
||||||
|
FIRST_BYTE_AS_FLAGS = 0x80,
|
||||||
|
}
|
||||||
|
|
||||||
|
"""
|
||||||
|
ファイルから読み込む
|
||||||
|
|
||||||
|
@param path ファイルパス
|
||||||
|
@return xm
|
||||||
|
"""
|
||||||
|
func read_file( path:String ) -> Mod.ModParseResult:
|
||||||
|
var f:File = File.new( )
|
||||||
|
var result: = Mod.ModParseResult.new( )
|
||||||
|
|
||||||
|
var err:int = f.open( path, f.READ )
|
||||||
|
if err != OK:
|
||||||
|
result.error = err
|
||||||
|
return result
|
||||||
|
|
||||||
|
var stream:StreamPeerBuffer = StreamPeerBuffer.new( )
|
||||||
|
stream.set_data_array( f.get_buffer( f.get_len( ) ) )
|
||||||
|
f.close( )
|
||||||
|
|
||||||
|
result.data = self._read( stream )
|
||||||
|
if result.data == null:
|
||||||
|
result.error = ERR_PARSE_ERROR
|
||||||
|
return result
|
||||||
|
|
||||||
|
"""
|
||||||
|
配列から読み込む
|
||||||
|
|
||||||
|
@param data データ
|
||||||
|
@return xm
|
||||||
|
"""
|
||||||
|
func read_data( data:PoolByteArray ) -> Mod.ModParseResult:
|
||||||
|
var stream:StreamPeerBuffer = StreamPeerBuffer.new( )
|
||||||
|
stream.set_data_array( data )
|
||||||
|
|
||||||
|
var result: = Mod.ModParseResult.new( )
|
||||||
|
result.data = self._read( stream )
|
||||||
|
if result.data == null:
|
||||||
|
result.error = ERR_PARSE_ERROR
|
||||||
|
return result
|
||||||
|
|
||||||
|
"""
|
||||||
|
ストリームから読み込み
|
||||||
|
|
||||||
|
@param stream ストリーム
|
||||||
|
@return xm
|
||||||
|
"""
|
||||||
|
func _read( stream:StreamPeerBuffer ) -> Mod.ModData:
|
||||||
|
var xm: = Mod.ModData.new( )
|
||||||
|
|
||||||
|
if self._read_string( stream, 17 ) != "Extended Module: ":
|
||||||
|
return null
|
||||||
|
xm.module_name = self._read_string( stream, 20 )
|
||||||
|
if stream.get_u8( ) != 0x1A:
|
||||||
|
return null
|
||||||
|
xm.tracker_name = self._read_string( stream, 20 )
|
||||||
|
xm.version = stream.get_u16( )
|
||||||
|
|
||||||
|
var header_size:int = stream.get_u32( )
|
||||||
|
xm.song_length = stream.get_u16( )
|
||||||
|
xm.restart_position = stream.get_u16( )
|
||||||
|
xm.channel_count = stream.get_u16( )
|
||||||
|
|
||||||
|
var pattern_count:int = stream.get_u16( )
|
||||||
|
var instrument_count:int = stream.get_u16( )
|
||||||
|
xm.flags = stream.get_u16( )
|
||||||
|
xm.init_tick = stream.get_u16( )
|
||||||
|
xm.init_bpm = stream.get_u16( )
|
||||||
|
|
||||||
|
xm.song_positions = stream.get_partial_data( header_size - 20 )[1]
|
||||||
|
|
||||||
|
xm.patterns = self._read_patterns( stream, xm.flags, pattern_count, xm.channel_count )
|
||||||
|
if len( xm.patterns ) == 0:
|
||||||
|
return null
|
||||||
|
|
||||||
|
xm.instruments = self._read_instruments( stream, instrument_count )
|
||||||
|
if len( xm.instruments ) == 0:
|
||||||
|
return null
|
||||||
|
|
||||||
|
return xm
|
||||||
|
|
||||||
|
"""
|
||||||
|
パターン読み込み
|
||||||
|
|
||||||
|
@param stream ストリーム
|
||||||
|
@param xm_file_flags XMデータに関するフラグ
|
||||||
|
@param pattern_count パターン数
|
||||||
|
@param channles 最大チャンネル数
|
||||||
|
@return パターンデータ
|
||||||
|
"""
|
||||||
|
func _read_patterns( stream:StreamPeerBuffer, xm_file_flags:int, pattern_count:int, channels:int ) -> Array:
|
||||||
|
var patterns:Array = []
|
||||||
|
|
||||||
|
for i in range( pattern_count ):
|
||||||
|
var pattern:Array = []
|
||||||
|
|
||||||
|
if stream.get_u32( ) != 9:
|
||||||
|
return []
|
||||||
|
if stream.get_u8( ) != 0:
|
||||||
|
return []
|
||||||
|
var row_count:int = stream.get_u16( )
|
||||||
|
var size:int = stream.get_u16( )
|
||||||
|
|
||||||
|
for k in range( row_count ):
|
||||||
|
var line:Array = []
|
||||||
|
for ch in range( channels ):
|
||||||
|
var patr: = Mod.ModPatternNote.new( )
|
||||||
|
var first:int = stream.get_u8( )
|
||||||
|
if first & XMNoteFlags.FIRST_BYTE_AS_FLAGS != 0:
|
||||||
|
if first & XMNoteFlags.NOTE != 0:
|
||||||
|
patr.note = stream.get_u8( )
|
||||||
|
else:
|
||||||
|
patr.note = first
|
||||||
|
first = 0xFF
|
||||||
|
if 0 < patr.note:
|
||||||
|
patr.note -= 1
|
||||||
|
if first & XMNoteFlags.INSTRUMENT != 0:
|
||||||
|
patr.instrument = stream.get_u8( )
|
||||||
|
if first & XMNoteFlags.VOLUME != 0:
|
||||||
|
patr.volume = stream.get_u8( )
|
||||||
|
if first & XMNoteFlags.EFFECT_COMMAND != 0:
|
||||||
|
patr.effect_command = stream.get_u8( )
|
||||||
|
if first & XMNoteFlags.EFFECT_PARAM != 0:
|
||||||
|
patr.effect_param = stream.get_u8( )
|
||||||
|
if 0 < patr.note:
|
||||||
|
if xm_file_flags & Mod.ModFlags.LINEAR_FREQUENCY_TABLE != 0:
|
||||||
|
patr.key_number = self._conv_linear_freq( patr.note )
|
||||||
|
else:
|
||||||
|
patr.key_number = self._conv_amiga_freq( patr.note )
|
||||||
|
line.append( patr )
|
||||||
|
pattern.append( line )
|
||||||
|
patterns.append( pattern )
|
||||||
|
|
||||||
|
return patterns
|
||||||
|
|
||||||
|
"""
|
||||||
|
ノート番号からAMIGA式で周波数計算する
|
||||||
|
|
||||||
|
@param note ノート番号
|
||||||
|
@return 周波数
|
||||||
|
"""
|
||||||
|
func _conv_amiga_freq( note:int ) -> int:
|
||||||
|
return int( 6848.0 / pow( 2.0, note / 12.0 ) )
|
||||||
|
|
||||||
|
"""
|
||||||
|
ノート番号から線形で周波数計算する
|
||||||
|
|
||||||
|
@param note ノート番号
|
||||||
|
@return 周波数
|
||||||
|
"""
|
||||||
|
func _conv_linear_freq( note:int ) -> int:
|
||||||
|
return 7680 - note * 64
|
||||||
|
|
||||||
|
"""
|
||||||
|
楽器データを読み込む
|
||||||
|
|
||||||
|
@param stream ストリーム
|
||||||
|
@param instrument_count 楽器数
|
||||||
|
@return 楽器データ
|
||||||
|
"""
|
||||||
|
func _read_instruments( stream:StreamPeerBuffer, instrument_count:int ) -> Array:
|
||||||
|
var instruments:Array = []
|
||||||
|
|
||||||
|
for i in range( instrument_count ):
|
||||||
|
var inst: = Mod.ModInstrument.new( )
|
||||||
|
var size:int = stream.get_u32( )
|
||||||
|
var remain_size:int = size
|
||||||
|
inst.name = self._read_string( stream, 22 )
|
||||||
|
#print( "%d/%d %s" % [ i, instrument_count, inst.name] )
|
||||||
|
if stream.get_u8( ) != 0:
|
||||||
|
return []
|
||||||
|
|
||||||
|
var sample_count:int = stream.get_u16( )
|
||||||
|
var sample_numbers:Array = []
|
||||||
|
remain_size -= 4 + 22 + 1 + 2
|
||||||
|
if 0 < sample_count:
|
||||||
|
var sample_header_size:int = stream.get_u32( )
|
||||||
|
sample_numbers = stream.get_partial_data( 96 )[1]
|
||||||
|
inst.volume_envelope = Mod.ModEnvelope.new( )
|
||||||
|
for k in range( 12 ):
|
||||||
|
var ve: = Mod.ModEnvelopePoint.new( )
|
||||||
|
ve.frame = stream.get_u16( )
|
||||||
|
ve.value = stream.get_u16( )
|
||||||
|
inst.volume_envelope.points.append( ve )
|
||||||
|
inst.panning_envelope = Mod.ModEnvelope.new( )
|
||||||
|
for k in range( 12 ):
|
||||||
|
var pe: = Mod.ModEnvelopePoint.new( )
|
||||||
|
pe.frame = stream.get_u16( )
|
||||||
|
pe.value = stream.get_u16( )
|
||||||
|
inst.panning_envelope.points.append( pe )
|
||||||
|
remain_size -= 4 + 96 + 48 + 48
|
||||||
|
inst.volume_envelope.point_count = stream.get_u8( )
|
||||||
|
inst.panning_envelope.point_count = stream.get_u8( )
|
||||||
|
inst.volume_envelope.sustain_point = stream.get_u8( )
|
||||||
|
inst.volume_envelope.loop_start_point = stream.get_u8( )
|
||||||
|
inst.volume_envelope.loop_end_point = stream.get_u8( )
|
||||||
|
inst.panning_envelope.sustain_point = stream.get_u8( )
|
||||||
|
inst.panning_envelope.loop_start_point = stream.get_u8( )
|
||||||
|
inst.panning_envelope.loop_end_point = stream.get_u8( )
|
||||||
|
inst.volume_envelope.set_flag( stream.get_u8( ) )
|
||||||
|
inst.panning_envelope.set_flag( stream.get_u8( ) )
|
||||||
|
inst.vibrato_type = stream.get_u8( )
|
||||||
|
inst.vibrato_speed = stream.get_u8( )
|
||||||
|
inst.vibrato_depth = stream.get_u8( )
|
||||||
|
inst.vibrato_depth_shift = stream.get_u8( )
|
||||||
|
inst.volume_fadeout = stream.get_u16( )
|
||||||
|
remain_size -= 16
|
||||||
|
|
||||||
|
if 0 < remain_size:
|
||||||
|
stream.get_partial_data( remain_size ) # reserved
|
||||||
|
|
||||||
|
if 0 < sample_count:
|
||||||
|
var sounds:Array = []
|
||||||
|
# Sound Header
|
||||||
|
for k in range( sample_count ):
|
||||||
|
var xms: = Mod.ModSample.new( )
|
||||||
|
xms.length = stream.get_u32( )
|
||||||
|
xms.loop_start = stream.get_u32( )
|
||||||
|
xms.loop_length = stream.get_u32( )
|
||||||
|
xms.volume = stream.get_u8( )
|
||||||
|
xms.finetune = stream.get_8( )
|
||||||
|
xms.loop_type = stream.get_u8( )
|
||||||
|
if xms.loop_type & 16 != 0:
|
||||||
|
xms.bit = 16
|
||||||
|
xms.loop_type &= 3
|
||||||
|
xms.panning = stream.get_u8( )
|
||||||
|
xms.relative_note = stream.get_8( )
|
||||||
|
stream.get_u8( ) # Reserved
|
||||||
|
xms.name = self._read_string( stream, 22 )
|
||||||
|
sounds.append( xms )
|
||||||
|
# Sound Data
|
||||||
|
for xms in sounds:
|
||||||
|
var d:Array = []
|
||||||
|
var p:int = 0
|
||||||
|
|
||||||
|
if xms.bit == 16:
|
||||||
|
for k in range( xms.length >> 1 ):
|
||||||
|
p += stream.get_16( )
|
||||||
|
d.append( p & 0xFF )
|
||||||
|
d.append( ( p >> 8 ) & 0xFF )
|
||||||
|
else:
|
||||||
|
for k in range( xms.length ):
|
||||||
|
p += stream.get_8( )
|
||||||
|
d.append( p & 0xFF )
|
||||||
|
|
||||||
|
xms.data = PoolByteArray( d )
|
||||||
|
|
||||||
|
inst.samples = []
|
||||||
|
for k in sample_numbers:
|
||||||
|
if len( sounds ) <= k:
|
||||||
|
inst.samples.append( sounds[0] )
|
||||||
|
else:
|
||||||
|
inst.samples.append( sounds[k] )
|
||||||
|
|
||||||
|
instruments.append( inst )
|
||||||
|
|
||||||
|
return instruments
|
||||||
|
|
||||||
|
"""
|
||||||
|
文字列の読み込み
|
||||||
|
|
||||||
|
@param stream ストリーム
|
||||||
|
@param size 文字列サイズ
|
||||||
|
@return 読み込んだ文字列を返す
|
||||||
|
"""
|
||||||
|
func _read_string( stream:StreamPeerBuffer, size:int ) -> String:
|
||||||
|
return stream.get_partial_data( size )[1].get_string_from_ascii( )
|
||||||
|
|
Binary file not shown.
After Width: | Height: | Size: 924 B |
|
@ -0,0 +1,35 @@
|
||||||
|
[remap]
|
||||||
|
|
||||||
|
importer="texture"
|
||||||
|
type="StreamTexture"
|
||||||
|
path="res://.import/icon.png-517e88a2da8173eba7cfa3edf0828c77.stex"
|
||||||
|
metadata={
|
||||||
|
"vram_texture": false
|
||||||
|
}
|
||||||
|
|
||||||
|
[deps]
|
||||||
|
|
||||||
|
source_file="res://addons/modplayer/icon.png"
|
||||||
|
dest_files=[ "res://.import/icon.png-517e88a2da8173eba7cfa3edf0828c77.stex" ]
|
||||||
|
|
||||||
|
[params]
|
||||||
|
|
||||||
|
compress/mode=0
|
||||||
|
compress/lossy_quality=0.7
|
||||||
|
compress/hdr_mode=0
|
||||||
|
compress/bptc_ldr=0
|
||||||
|
compress/normal_map=0
|
||||||
|
flags/repeat=0
|
||||||
|
flags/filter=false
|
||||||
|
flags/mipmaps=false
|
||||||
|
flags/anisotropic=false
|
||||||
|
flags/srgb=2
|
||||||
|
process/fix_alpha_border=true
|
||||||
|
process/premult_alpha=false
|
||||||
|
process/HDR_as_SRGB=false
|
||||||
|
process/invert_color=false
|
||||||
|
process/normal_map_invert_y=false
|
||||||
|
stream=false
|
||||||
|
size_limit=0
|
||||||
|
detect_3d=false
|
||||||
|
svg/scale=1.0
|
|
@ -0,0 +1,6 @@
|
||||||
|
[plugin]
|
||||||
|
name="Godot Mod Player"
|
||||||
|
description="A plugin that playing Mod Files for Godot Engine."
|
||||||
|
author="arlez80"
|
||||||
|
version="1.4.4"
|
||||||
|
script="GMP.gd"
|
Binary file not shown.
|
@ -0,0 +1,73 @@
|
||||||
|
[gd_scene load_steps=7 format=2]
|
||||||
|
|
||||||
|
[ext_resource path="res://icon.png" type="Texture" id=1]
|
||||||
|
[ext_resource path="res://addons/platformer_controller/platformer_controller.gd" type="Script" id=2]
|
||||||
|
|
||||||
|
[sub_resource type="RectangleShape2D" id=1]
|
||||||
|
extents = Vector2( 512, 56 )
|
||||||
|
|
||||||
|
[sub_resource type="RectangleShape2D" id=2]
|
||||||
|
extents = Vector2( 208, 56 )
|
||||||
|
|
||||||
|
[sub_resource type="RectangleShape2D" id=3]
|
||||||
|
extents = Vector2( 32, 304 )
|
||||||
|
|
||||||
|
[sub_resource type="RectangleShape2D" id=4]
|
||||||
|
extents = Vector2( 32, 32 )
|
||||||
|
|
||||||
|
[node name="Main" type="Node2D"]
|
||||||
|
|
||||||
|
[node name="StaticBody2D" type="StaticBody2D" parent="."]
|
||||||
|
position = Vector2( 520, 544 )
|
||||||
|
|
||||||
|
[node name="ColorRect" type="ColorRect" parent="StaticBody2D"]
|
||||||
|
margin_left = -520.0
|
||||||
|
margin_top = -56.0
|
||||||
|
margin_right = 504.0
|
||||||
|
margin_bottom = 56.0
|
||||||
|
color = Color( 0, 0.443137, 0.027451, 1 )
|
||||||
|
__meta__ = {
|
||||||
|
"_edit_use_anchors_": false
|
||||||
|
}
|
||||||
|
|
||||||
|
[node name="CollisionShape2D" type="CollisionShape2D" parent="StaticBody2D"]
|
||||||
|
position = Vector2( -8, 0 )
|
||||||
|
shape = SubResource( 1 )
|
||||||
|
|
||||||
|
[node name="StaticBody2D2" type="StaticBody2D" parent="."]
|
||||||
|
position = Vector2( 520, 360 )
|
||||||
|
|
||||||
|
[node name="ColorRect" type="ColorRect" parent="StaticBody2D2"]
|
||||||
|
margin_left = -520.0
|
||||||
|
margin_top = 16.0
|
||||||
|
margin_right = -104.0
|
||||||
|
margin_bottom = 128.0
|
||||||
|
color = Color( 0, 0.443137, 0.027451, 1 )
|
||||||
|
__meta__ = {
|
||||||
|
"_edit_use_anchors_": false
|
||||||
|
}
|
||||||
|
|
||||||
|
[node name="CollisionShape2D" type="CollisionShape2D" parent="StaticBody2D2"]
|
||||||
|
position = Vector2( -312, 72 )
|
||||||
|
shape = SubResource( 2 )
|
||||||
|
|
||||||
|
[node name="StaticBody2D3" type="StaticBody2D" parent="."]
|
||||||
|
position = Vector2( 520, 360 )
|
||||||
|
|
||||||
|
[node name="CollisionShape2D" type="CollisionShape2D" parent="StaticBody2D3"]
|
||||||
|
position = Vector2( -552, -64 )
|
||||||
|
shape = SubResource( 3 )
|
||||||
|
|
||||||
|
[node name="CollisionShape2D2" type="CollisionShape2D" parent="StaticBody2D3"]
|
||||||
|
position = Vector2( 536, -64 )
|
||||||
|
shape = SubResource( 3 )
|
||||||
|
|
||||||
|
[node name="PlatformerController2D" type="KinematicBody2D" parent="."]
|
||||||
|
position = Vector2( 272, 167 )
|
||||||
|
script = ExtResource( 2 )
|
||||||
|
|
||||||
|
[node name="icon" type="Sprite" parent="PlatformerController2D"]
|
||||||
|
texture = ExtResource( 1 )
|
||||||
|
|
||||||
|
[node name="CollisionShape2D" type="CollisionShape2D" parent="PlatformerController2D"]
|
||||||
|
shape = SubResource( 4 )
|
|
@ -0,0 +1,21 @@
|
||||||
|
MIT License
|
||||||
|
|
||||||
|
Copyright (c) 2021 Ev01
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
SOFTWARE.
|
|
@ -0,0 +1,75 @@
|
||||||
|
# PlatformerController2D
|
||||||
|
|
||||||
|
A 2D platformer class for godot.
|
||||||
|
|
||||||
|
## Changelog
|
||||||
|
### Version 1.0.1
|
||||||
|
- Updated to Godot 3.4
|
||||||
|
- Fixed division by zero error when changing min jump height
|
||||||
|
- Other minor fixes
|
||||||
|
|
||||||
|
## Installation
|
||||||
|
|
||||||
|
1. Add platformer_controller.gd to your project
|
||||||
|
2. Type `extends PlatformerController2D` to the top of your script
|
||||||
|
3. Add these input mappings in your project settings (or you can change the input variables in the inspector)
|
||||||
|
- "move_left"
|
||||||
|
- "move_right"
|
||||||
|
- "jump"
|
||||||
|
|
||||||
|
|
||||||
|
## Features
|
||||||
|
- Double jump
|
||||||
|
- Coyote time
|
||||||
|
- Jump buffer
|
||||||
|
- Hold jump to go higher
|
||||||
|
- Defining jump height and duration (as opposed to setting gravity and jump velocity)
|
||||||
|
- Assymetrical jumps (falling faster than rising)
|
||||||
|
|
||||||
|
<img src="https://github.com/Ev01/PlatformerController2D/raw/assets/jumping.GIF" width="500">| <img src="https://github.com/Ev01/PlatformerController2D/raw/assets/jump_duration.GIF" width="500"> | <img src="https://github.com/Ev01/PlatformerController2D/raw/assets/jump_height.GIF" width="500">
|
||||||
|
--|--|--
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## Customization / Export variables
|
||||||
|
Here are the values that you can change in the inspector:
|
||||||
|
|
||||||
|
### max_jump_height
|
||||||
|
The max jump height in pixels. You reach this when you hold down jump.
|
||||||
|
|
||||||
|
### Min Jump Height
|
||||||
|
The minimum jump height (tapping jump).
|
||||||
|
|
||||||
|
### Double Jump Height
|
||||||
|
The height of your jump in the air (i.e. double jump, triple jump etc.).
|
||||||
|
|
||||||
|
### Jump Duration
|
||||||
|
How long it takes to get to the peak of the jump (in seconds).
|
||||||
|
|
||||||
|
### Falling Gravity Multiplier
|
||||||
|
Multiplies the gravity by this while falling.
|
||||||
|
|
||||||
|
### Max Jump Amount
|
||||||
|
How many times you can jump before hitting the ground. Set this to 2 for a double jump.
|
||||||
|
|
||||||
|
### Max Acceleration
|
||||||
|
How much you accelerate when you hold left or right (in pixels/sec^2).
|
||||||
|
|
||||||
|
### Friction
|
||||||
|
The higher this number, the more friction is on your character.
|
||||||
|
|
||||||
|
### Can Hold Jump
|
||||||
|
If this is off, you have to press jump down every time you land. If its on you can keep it held.
|
||||||
|
|
||||||
|
### Coyote Time
|
||||||
|
You can still jump this many seconds after falling off a ledge.
|
||||||
|
|
||||||
|
### Jump Buffer
|
||||||
|
Pressing jump this many seconds before hitting the ground will still make you jump.\
|
||||||
|
Note: This is only needed when can_hold_jump is off.
|
||||||
|
|
||||||
|
### Input Variables
|
||||||
|
`input_left`\
|
||||||
|
`input_right`\
|
||||||
|
`input_jump`\
|
||||||
|
Set these to the names of your actions in the Input Map
|
|
@ -0,0 +1,213 @@
|
||||||
|
extends KinematicBody2D
|
||||||
|
|
||||||
|
class_name PlatformerController2D
|
||||||
|
|
||||||
|
# Set these to the name of your action (in the Input Map)
|
||||||
|
export var input_left : String = "move_left"
|
||||||
|
export var input_right : String = "move_right"
|
||||||
|
export var input_jump : String = "jump"
|
||||||
|
|
||||||
|
# The max jump height in pixels (holding jump)
|
||||||
|
export var max_jump_height = 150 setget set_max_jump_height
|
||||||
|
# The minimum jump height (tapping jump)
|
||||||
|
export var min_jump_height = 40 setget set_min_jump_height
|
||||||
|
# The height of your jump in the air
|
||||||
|
export var double_jump_height = 100 setget set_double_jump_height
|
||||||
|
# How long it takes to get to the peak of the jump in seconds
|
||||||
|
export var jump_duration = 0.3 setget set_jump_duration
|
||||||
|
# Multiplies the gravity by this while falling
|
||||||
|
export var falling_gravity_multiplier = 1.5
|
||||||
|
# Set to 2 for double jump
|
||||||
|
export var max_jump_amount = 1
|
||||||
|
export var max_acceleration = 4000
|
||||||
|
export var friction = 8
|
||||||
|
export var can_hold_jump : bool = false
|
||||||
|
# You can still jump this many seconds after falling off a ledge
|
||||||
|
export var coyote_time : float = 0.1
|
||||||
|
# Only neccessary when can_hold_jump is off
|
||||||
|
# Pressing jump this many seconds before hitting the ground will still make you jump
|
||||||
|
export var jump_buffer : float = 0.1
|
||||||
|
|
||||||
|
|
||||||
|
# not used
|
||||||
|
var max_speed = 100
|
||||||
|
var acceleration_time = 10
|
||||||
|
|
||||||
|
|
||||||
|
# These will be calcualted automatically
|
||||||
|
var default_gravity : float
|
||||||
|
var jump_velocity : float
|
||||||
|
var double_jump_velocity : float
|
||||||
|
# Multiplies the gravity by this when we release jump
|
||||||
|
var release_gravity_multiplier : float
|
||||||
|
|
||||||
|
|
||||||
|
var jumps_left : int
|
||||||
|
var holding_jump := false
|
||||||
|
|
||||||
|
var vel = Vector2()
|
||||||
|
var acc = Vector2()
|
||||||
|
|
||||||
|
onready var coyote_timer = Timer.new()
|
||||||
|
onready var jump_buffer_timer = Timer.new()
|
||||||
|
|
||||||
|
|
||||||
|
func _init():
|
||||||
|
default_gravity = calculate_gravity(max_jump_height, jump_duration)
|
||||||
|
jump_velocity = calculate_jump_velocity(max_jump_height, jump_duration)
|
||||||
|
double_jump_velocity = calculate_jump_velocity2(double_jump_height, default_gravity)
|
||||||
|
release_gravity_multiplier = calculate_release_gravity_multiplier(
|
||||||
|
jump_velocity, min_jump_height, default_gravity)
|
||||||
|
|
||||||
|
|
||||||
|
func _ready():
|
||||||
|
add_child(coyote_timer)
|
||||||
|
coyote_timer.wait_time = coyote_time
|
||||||
|
coyote_timer.one_shot = true
|
||||||
|
|
||||||
|
add_child(jump_buffer_timer)
|
||||||
|
jump_buffer_timer.wait_time = jump_buffer
|
||||||
|
jump_buffer_timer.one_shot = true
|
||||||
|
|
||||||
|
|
||||||
|
func _physics_process(delta):
|
||||||
|
acc.x = 0
|
||||||
|
|
||||||
|
if is_on_floor():
|
||||||
|
coyote_timer.start()
|
||||||
|
if not coyote_timer.is_stopped():
|
||||||
|
jumps_left = max_jump_amount
|
||||||
|
|
||||||
|
if Input.is_action_pressed(input_left):
|
||||||
|
acc.x = -max_acceleration
|
||||||
|
if Input.is_action_pressed(input_right):
|
||||||
|
acc.x = max_acceleration
|
||||||
|
|
||||||
|
|
||||||
|
# Check for ground jumps when we can hold jump
|
||||||
|
if can_hold_jump:
|
||||||
|
if Input.is_action_pressed(input_jump):
|
||||||
|
# Dont use double jump when holding down
|
||||||
|
if is_on_floor():
|
||||||
|
jump()
|
||||||
|
|
||||||
|
# Check for ground jumps when we cannot hold jump
|
||||||
|
if not can_hold_jump:
|
||||||
|
if not jump_buffer_timer.is_stopped() and is_on_floor():
|
||||||
|
jump()
|
||||||
|
|
||||||
|
# Check for jumps in the air
|
||||||
|
if Input.is_action_just_pressed(input_jump):
|
||||||
|
holding_jump = true
|
||||||
|
jump_buffer_timer.start()
|
||||||
|
|
||||||
|
# Only jump in the air when press the button down, code above already jumps when we are grounded
|
||||||
|
if not is_on_floor():
|
||||||
|
jump()
|
||||||
|
|
||||||
|
|
||||||
|
if Input.is_action_just_released(input_jump):
|
||||||
|
holding_jump = false
|
||||||
|
|
||||||
|
|
||||||
|
var gravity = default_gravity
|
||||||
|
|
||||||
|
if vel.y > 0: # If we are falling
|
||||||
|
gravity *= falling_gravity_multiplier
|
||||||
|
|
||||||
|
if not holding_jump and vel.y < 0: # if we released jump and are still rising
|
||||||
|
if not jumps_left < max_jump_amount - 1: # Always jump to max height when we are using a double jump
|
||||||
|
gravity *= release_gravity_multiplier # multiply the gravity so we have a lower jump
|
||||||
|
|
||||||
|
acc.y = -gravity
|
||||||
|
vel.x *= 1 / (1 + (delta * friction))
|
||||||
|
|
||||||
|
vel += acc * delta
|
||||||
|
vel = move_and_slide(vel, Vector2.UP)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
func calculate_gravity(p_max_jump_height, p_jump_duration):
|
||||||
|
# Calculates the desired gravity by looking at our jump height and jump duration
|
||||||
|
# Formula is from this video https://www.youtube.com/watch?v=hG9SzQxaCm8
|
||||||
|
return (-2 *p_max_jump_height) / pow(p_jump_duration, 2)
|
||||||
|
|
||||||
|
|
||||||
|
func calculate_jump_velocity(p_max_jump_height, p_jump_duration):
|
||||||
|
# Calculates the desired jump velocity by lookihg at our jump height and jump duration
|
||||||
|
return (2 * p_max_jump_height) / (p_jump_duration)
|
||||||
|
|
||||||
|
|
||||||
|
func calculate_jump_velocity2(p_max_jump_height, p_gravity):
|
||||||
|
# Calculates jump velocity from jump height and gravity
|
||||||
|
# formula from
|
||||||
|
# https://sciencing.com/acceleration-velocity-distance-7779124.html#:~:text=in%20every%20step.-,Starting%20from%3A,-v%5E2%3Du
|
||||||
|
return sqrt(-2 * p_gravity * p_max_jump_height)
|
||||||
|
|
||||||
|
|
||||||
|
func calculate_release_gravity_multiplier(p_jump_velocity, p_min_jump_height, p_gravity):
|
||||||
|
# Calculates the gravity when the key is released based on the minimum jump height and jump velocity
|
||||||
|
# Formula is from this website https://sciencing.com/acceleration-velocity-distance-7779124.html
|
||||||
|
var release_gravity = 0 - pow(p_jump_velocity, 2) / (2 * p_min_jump_height)
|
||||||
|
return release_gravity / p_gravity
|
||||||
|
|
||||||
|
|
||||||
|
func calculate_friction(time_to_max):
|
||||||
|
# Formula from https://www.reddit.com/r/gamedev/comments/bdbery/comment/ekxw9g4/?utm_source=share&utm_medium=web2x&context=3
|
||||||
|
# this friction will hit 90% of max speed after the accel time
|
||||||
|
return 1 - (2.30259 / time_to_max)
|
||||||
|
|
||||||
|
|
||||||
|
func calculate_speed(p_max_speed, p_friction):
|
||||||
|
# Formula from https://www.reddit.com/r/gamedev/comments/bdbery/comment/ekxw9g4/?utm_source=share&utm_medium=web2x&context=3
|
||||||
|
return (p_max_speed / p_friction) - p_max_speed
|
||||||
|
|
||||||
|
|
||||||
|
func jump():
|
||||||
|
if jumps_left == max_jump_amount and coyote_timer.is_stopped():
|
||||||
|
# Your first jump must be used when on the ground
|
||||||
|
# If you fall off the ground and then jump you will be using you second jump
|
||||||
|
jumps_left -= 1
|
||||||
|
|
||||||
|
if jumps_left > 0:
|
||||||
|
if jumps_left < max_jump_amount: # If we are double jumping
|
||||||
|
vel.y = -double_jump_velocity
|
||||||
|
else:
|
||||||
|
vel.y = -jump_velocity
|
||||||
|
jumps_left -= 1
|
||||||
|
|
||||||
|
|
||||||
|
coyote_timer.stop()
|
||||||
|
|
||||||
|
|
||||||
|
func set_max_jump_height(value):
|
||||||
|
max_jump_height = value
|
||||||
|
|
||||||
|
default_gravity = calculate_gravity(max_jump_height, jump_duration)
|
||||||
|
jump_velocity = calculate_jump_velocity(max_jump_height, jump_duration)
|
||||||
|
double_jump_velocity = calculate_jump_velocity2(double_jump_height, default_gravity)
|
||||||
|
release_gravity_multiplier = calculate_release_gravity_multiplier(
|
||||||
|
jump_velocity, min_jump_height, default_gravity)
|
||||||
|
|
||||||
|
|
||||||
|
func set_jump_duration(value):
|
||||||
|
jump_duration = value
|
||||||
|
|
||||||
|
default_gravity = calculate_gravity(max_jump_height, jump_duration)
|
||||||
|
jump_velocity = calculate_jump_velocity(max_jump_height, jump_duration)
|
||||||
|
double_jump_velocity = calculate_jump_velocity2(double_jump_height, default_gravity)
|
||||||
|
release_gravity_multiplier = calculate_release_gravity_multiplier(
|
||||||
|
jump_velocity, min_jump_height, default_gravity)
|
||||||
|
|
||||||
|
|
||||||
|
func set_min_jump_height(value):
|
||||||
|
min_jump_height = value
|
||||||
|
release_gravity_multiplier = calculate_release_gravity_multiplier(
|
||||||
|
jump_velocity, min_jump_height, default_gravity)
|
||||||
|
|
||||||
|
|
||||||
|
func set_double_jump_height(value):
|
||||||
|
double_jump_height = value
|
||||||
|
double_jump_velocity = calculate_jump_velocity2(double_jump_height, default_gravity)
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
[gd_scene load_steps=12 format=2]
|
[gd_scene load_steps=12 format=2]
|
||||||
|
|
||||||
[ext_resource path="res://assets/images/Clean-Retro-Lines-Player-Plain.png" type="Texture" id=1]
|
[ext_resource path="res://assets/images/Clean-Retro-Lines-Player-Plain.png" type="Texture" id=1]
|
||||||
[ext_resource path="res://player/Player.gd" type="Script" id=2]
|
[ext_resource path="res://addons/platformer_controller/platformer_controller.gd" type="Script" id=2]
|
||||||
|
|
||||||
[sub_resource type="AtlasTexture" id=5]
|
[sub_resource type="AtlasTexture" id=5]
|
||||||
atlas = ExtResource( 1 )
|
atlas = ExtResource( 1 )
|
||||||
|
@ -11,10 +11,6 @@ region = Rect2( 0, 16, 16, 16 )
|
||||||
atlas = ExtResource( 1 )
|
atlas = ExtResource( 1 )
|
||||||
region = Rect2( 16, 16, 16, 16 )
|
region = Rect2( 16, 16, 16, 16 )
|
||||||
|
|
||||||
[sub_resource type="AtlasTexture" id=7]
|
|
||||||
atlas = ExtResource( 1 )
|
|
||||||
region = Rect2( 16, 32, 16, 16 )
|
|
||||||
|
|
||||||
[sub_resource type="AtlasTexture" id=1]
|
[sub_resource type="AtlasTexture" id=1]
|
||||||
atlas = ExtResource( 1 )
|
atlas = ExtResource( 1 )
|
||||||
region = Rect2( 0, 0, 16, 16 )
|
region = Rect2( 0, 0, 16, 16 )
|
||||||
|
@ -31,6 +27,10 @@ region = Rect2( 32, 0, 16, 16 )
|
||||||
atlas = ExtResource( 1 )
|
atlas = ExtResource( 1 )
|
||||||
region = Rect2( 48, 0, 16, 16 )
|
region = Rect2( 48, 0, 16, 16 )
|
||||||
|
|
||||||
|
[sub_resource type="AtlasTexture" id=7]
|
||||||
|
atlas = ExtResource( 1 )
|
||||||
|
region = Rect2( 16, 32, 16, 16 )
|
||||||
|
|
||||||
[sub_resource type="SpriteFrames" id=8]
|
[sub_resource type="SpriteFrames" id=8]
|
||||||
animations = [ {
|
animations = [ {
|
||||||
"frames": [ SubResource( 5 ), SubResource( 6 ) ],
|
"frames": [ SubResource( 5 ), SubResource( 6 ) ],
|
||||||
|
@ -38,15 +38,15 @@ animations = [ {
|
||||||
"name": "idle",
|
"name": "idle",
|
||||||
"speed": 2.0
|
"speed": 2.0
|
||||||
}, {
|
}, {
|
||||||
"frames": [ SubResource( 7 ) ],
|
|
||||||
"loop": true,
|
|
||||||
"name": "jump",
|
|
||||||
"speed": 5.0
|
|
||||||
}, {
|
|
||||||
"frames": [ SubResource( 1 ), SubResource( 2 ), SubResource( 3 ), SubResource( 4 ) ],
|
"frames": [ SubResource( 1 ), SubResource( 2 ), SubResource( 3 ), SubResource( 4 ) ],
|
||||||
"loop": true,
|
"loop": true,
|
||||||
"name": "walk",
|
"name": "walk",
|
||||||
"speed": 5.0
|
"speed": 5.0
|
||||||
|
}, {
|
||||||
|
"frames": [ SubResource( 7 ) ],
|
||||||
|
"loop": true,
|
||||||
|
"name": "jump",
|
||||||
|
"speed": 5.0
|
||||||
} ]
|
} ]
|
||||||
|
|
||||||
[sub_resource type="RectangleShape2D" id=9]
|
[sub_resource type="RectangleShape2D" id=9]
|
||||||
|
@ -54,10 +54,16 @@ extents = Vector2( 4, 7 )
|
||||||
|
|
||||||
[node name="Player" type="KinematicBody2D"]
|
[node name="Player" type="KinematicBody2D"]
|
||||||
script = ExtResource( 2 )
|
script = ExtResource( 2 )
|
||||||
|
max_jump_height = 50
|
||||||
|
min_jump_height = 25
|
||||||
|
double_jump_height = 65
|
||||||
|
max_jump_amount = 2
|
||||||
|
max_acceleration = 1500
|
||||||
|
|
||||||
[node name="AnimatedSprite" type="AnimatedSprite" parent="."]
|
[node name="AnimatedSprite" type="AnimatedSprite" parent="."]
|
||||||
frames = SubResource( 8 )
|
frames = SubResource( 8 )
|
||||||
animation = "idle"
|
animation = "idle"
|
||||||
|
frame = 1
|
||||||
playing = true
|
playing = true
|
||||||
|
|
||||||
[node name="CollisionShape2D" type="CollisionShape2D" parent="."]
|
[node name="CollisionShape2D" type="CollisionShape2D" parent="."]
|
||||||
|
|
|
@ -8,6 +8,34 @@
|
||||||
|
|
||||||
config_version=4
|
config_version=4
|
||||||
|
|
||||||
|
_global_script_classes=[ {
|
||||||
|
"base": "Reference",
|
||||||
|
"class": "Mod",
|
||||||
|
"language": "GDScript",
|
||||||
|
"path": "res://addons/modplayer/Mod.gd"
|
||||||
|
}, {
|
||||||
|
"base": "Node",
|
||||||
|
"class": "ModPlayer",
|
||||||
|
"language": "GDScript",
|
||||||
|
"path": "res://addons/modplayer/ModPlayer.gd"
|
||||||
|
}, {
|
||||||
|
"base": "KinematicBody2D",
|
||||||
|
"class": "PlatformerController2D",
|
||||||
|
"language": "GDScript",
|
||||||
|
"path": "res://addons/platformer_controller/platformer_controller.gd"
|
||||||
|
}, {
|
||||||
|
"base": "Reference",
|
||||||
|
"class": "XM",
|
||||||
|
"language": "GDScript",
|
||||||
|
"path": "res://addons/modplayer/XM.gd"
|
||||||
|
} ]
|
||||||
|
_global_script_class_icons={
|
||||||
|
"Mod": "",
|
||||||
|
"ModPlayer": "res://addons/modplayer/icon.png",
|
||||||
|
"PlatformerController2D": "",
|
||||||
|
"XM": ""
|
||||||
|
}
|
||||||
|
|
||||||
[application]
|
[application]
|
||||||
|
|
||||||
config/name="Retro Platformer"
|
config/name="Retro Platformer"
|
||||||
|
@ -15,6 +43,7 @@ run/main_scene="res://levels/Level01.tscn"
|
||||||
boot_splash/image="res://assets/images/WithinLogo.png"
|
boot_splash/image="res://assets/images/WithinLogo.png"
|
||||||
boot_splash/fullsize=false
|
boot_splash/fullsize=false
|
||||||
boot_splash/use_filter=false
|
boot_splash/use_filter=false
|
||||||
|
boot_splash/bg_color=Color( 0.141176, 0.141176, 0.141176, 1 )
|
||||||
config/icon="res://icon.png"
|
config/icon="res://icon.png"
|
||||||
|
|
||||||
[autoload]
|
[autoload]
|
||||||
|
|
Loading…
Reference in New Issue